3
\$\begingroup\$

I have some code which uses nested foreach loops to take the results of a query and drill down into the data, reassigning into a main data array at each level of the foreach nesting. The problem is that required reference variables are passed into the loops deeper down the tree, making it difficult to refactor.

I'd like to refactor this into a more functional style. I've looked into array_walk() and array_map() to try and accomplish this but they seem to be more suited to amending the array argument rather than reassigning to an external array.

I'm looking more for general approaches to refactoring out foreach loops in PHP with this sort of context rather than a direct solution to my problem.

foreach($grid_fields as $gridFieldKey => $gridFieldValue) {
    $grid_field_id = $gridFieldValue['field_id'];
    $grid_field_str = 'field_id_' . $grid_field_id;
    $grid_entry = $this->fetchGridEntry($grid_field_id, $data['entry_id']);

    if(sizeOf($grid_entry) > 0) {
        $data[$grid_field_str] = array();
        $data[$grid_field_str]['entry_id'] = $cloned;

        foreach($grid_entry as $gridEntryKey => $gridEntryValue) {
            $grid_row = 'new_row_' . $gridEntryKey;
            $data[$grid_field_str]['rows'][$grid_row] = array();

            foreach($gridEntryValue as $geK => $geV) {
                if(strpos($geK, 'col_id_') !== false || strpos($geK, 'row_id') !== false) {
                    $data[$grid_field_str]['rows'][$grid_row][$geK] = $geV;
                }
            }
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Can you provide more context? What real world problem are you solving (it sounds like perhaps updating existing data structure with information from data store)? Where do $grid_fields and $data come from? Can you provide an example of the data structure that you are working with? My guess is that in addition to general array function concepts (well explained in answer from @arcanine below), that you actually have some data structure problems that, if addressed, might simplify what you are trying to do. Just splitting this code apart into functions might not be a desirable refactoring. \$\endgroup\$ – Mike Brant Aug 8 '17 at 21:25
3
\$\begingroup\$
  • array_filter removes elements from an array
  • array_map changes each element of an array to something else
  • array_walk steps through each element of an array (usually to create side effects) (does not return anything)
  • array_reduce create something new from an existing array

Every traditional loop (that does one thing) is one of the above.


<?php
// Inside your object.
$data = array_reduce($grid_fields, add_grid_fields_to_data($cloned, $this), $data);

// Outside your object.
function add_grid_fields_to_data($cloned, $object) : array {
    return (function (array $data, array $grid_field) use ($cloned) {

        $grid_entry = $this->fetchGridEntry($grid_field['field_id'], $data['entry_id']);
        $grid_field_str = 'field_id_' . $grid_field_id;

        $data[$grid_field_str] = array();
        $data[$grid_field_str]['entry_id'] = $cloned;

        $no_grid_entry = sizeOf($grid_entry) == 0;
        $no_grid_entry ?: $data = array_reduce(array_keys($grid_entry), add_grid_entry_values_to_data($grid_entry, $grid_field_str), $data);

        return $data;
    })->bindTo($object);
}

function add_grid_entry_values_to_data(array $grid_entry, $grid_field_str) : Closure {
  return function (array $data, string $gridEntryKey) use ($grid_entry, $grid_field_str) : array {
    $gridEntryValue = $grid_entry[$gridEntryKey];
    $grid_row = 'new_row_' . $gridEntryKey;

    $data[$grid_field_str]['rows'][$grid_row] = array();

    $data = array_reduce(array_keys($gridEntryValue), add_grid_row_to_data($grid_row, $gridEntryValue), $data);
    return $data;  
  };
}

function add_grid_row_to_data(string $grid_row, array $gridEntryValue) : Closure {
    return function (array $data, $geK) use ($grid_row, $gridEntryValue) : data {
        $geV = $gridEntryvalue[$geK];

        $row_col_id_exists = strpos($geK, 'col_id_') !== FALSE || strpos($geK, 'row_id') !== FALSE;
        $row_col_id_exists ? $data[$grid_field_str]['rows'][$grid_row][$geK] = $geV : NULL;

        return $data;
    }; 
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.