3
\$\begingroup\$

I have an extremely large function for turning my database output of menu links into a multidimensional array that nests each of the links in a menu fashion. I'm wondering if anyone sees a way this class function can be shortened or simplified in some way.

The mysqli query:

private function _interactions($product, $permissions, $mymenu){
    $myMenuArray = !empty($mymenu) ? explode(",", $mymenu) : NULL;
    $myDBMenuArray = array();
    $stmt = $this->mysqli->select("SELECT * FROM skss_".$product."_menu WHERE permissions >= ?", array($permissions), array("%i"));
    if(count($stmt)>0 && empty($myMenuArray)){
        array_walk($stmt, (function(&$item){$item = (array)$item;}));
        $myDBMenuArray = array_merge($stmt);
    }
    else if(count($stmt)>0){
        foreach($stmt as $obj){
            $found = array_search($obj->data_target, $myMenuArray);
            if($found !== FALSE){ $myDBMenuArray[] = (array)$obj; }
        }
    }
    return $this->_buildMenuAndActions($myDBMenuArray);
}

Which returns this array from the DB:

Array
 (
     [0] => Array
         (
             [data_target] => '#address-modal'
             [ancor_value] => 'Address Dialog'
             [inmenu] => 0
             [static] => 1
             [keyboard] => 1
             [name] => 'address-modal-link'
             [href] => 
             [menuID] => 
             [permissions] => 9
         )

     [1] => Array
         (
             [data_target] => '#error-modal'
             [ancor_value] => 'Error Modal'
             [inmenu] => 0
             [static] => 1
             [keyboard] => 1
             [name] => 'error-modal-link'
             [href] => 
             [menuID] => 
             [permissions] => 9
         )

     [2] => Array
         (
             [data_target] => '#message-modal'
             [ancor_value] => 'Message Modal'
             [inmenu] => 0
             [static] => 1
             [keyboard] => 0
             [name] => 'message-modal-link'
             [href] => 
             [menuID] => 
             [permissions] => 9
         )

     [3] => Array
         (
             [data_target] => '#mytarget'
             [ancor_value] => 'unknown'
             [inmenu] => 0
             [static] => 1
             [keyboard] => 1
             [name] => 'mytarget'
             [href] => 
             [menuID] => '#address-modal'
             [permissions] => 9
         )

     [4] => Array
         (
             [data_target] => '#mytarget2'
             [ancor_value] => 'mytarget'
             [inmenu] => 0
             [static] => 1
             [keyboard] => 1
             [name] => 'mytarget'
             [href] => 
             [menuID] => '#address-modal'
             [permissions] => 0
         )

     [5] => Array
         (
             [data_target] => '#mytarget3'
             [ancor_value] => 'mytarget3'
             [inmenu] => 0
             [static] => 1
             [keyboard] => 1
             [name] => 'mytarget3'
             [href] => 
             [menuID] => '#mytarget'
             [permissions] => 9
         )

     [6] => Array
         (
             [data_target] => '#user-modal'
             [ancor_value] => 'User Dialog'
             [inmenu] => 0
             [static] => 1
             [keyboard] => 1
             [name] => 'user-modal-link'
             [href] => 
             [menuID] => 
             [permissions] => 9
         )

     [7] => Array
         (
             [data_target] => '#userImage-modal'
             [ancor_value] => 'User Image Modal'
             [inmenu] => 0
             [static] => 1
             [keyboard] => 0
             [name] => 'userImage-modal-link'
             [href] => 
             [menuID] => 
             [permissions] => 9
         )

     [8] => Array
         (
             [data_target] => '#wait-modal'
             [ancor_value] => 'Wait/Loading Modal'
             [inmenu] => 0
             [static] => 1
             [keyboard] => 0
             [name] => 'wait-modal-link'
             [href] => 
             [menuID] => 
             [permissions] => 9
         )

 )

This is my rather very complicated class function for turning all my links into menu nested items.

private function _buildMenuAndActions($interactions, $myList = array()){
    $myMenu = array();
    foreach($interactions as $interaction){
        // First layers.
        // If $interaction['menuID'] would ever purposely be a zero (for 
        // instance as a numerical row ID), this would need to be a 
        // "=== NULL" comparison and NULL would need to be maintained 
        // on the DB.  
        if(empty($interaction['menuID'])){
            $searchFor = $interaction['data_target'];
            $filteredArray = 
            array_filter($interactions, function($element) use($searchFor){
              return isset($element['menuID']) && $element['menuID'] == $searchFor;
            });
            $myMenu[] = array("link" => $this->_create($interaction), "children" => !empty($filteredArray) ? $this->_buildMenuAndActions($filteredArray, $interactions) : array());
        }
        // Layers with nestable layers.
        else if(
                    !empty($myList) && (array_search($interaction['data_target'], array_column($myList, 'menuID')) !== FALSE)
                ){
            $searchFor = $interaction['data_target'];
            $filteredArray = 
            array_filter($myList, function($element) use($searchFor){
              return isset($element['menuID']) && $element['menuID'] == $searchFor;
            });
            $myMenu[] = array("link" => $this->_create($interaction), "children" => !empty($filteredArray) ? $this->_buildMenuAndActions($filteredArray, $myList) : array());
        }
        // Layers without nestable layers.
        else if(
                    !empty($myList) && !(array_search($interaction['data_target'], array_column($myList, 'menuID')) !== FALSE)
                ){
            $searchFor = $interaction['data_target'];
            $filteredArray = 
            array_filter($interactions, function($element) use($searchFor){
              return isset($element['menuID']) && $element['menuID'] == $searchFor;
            });
            $myMenu[] = array("link" => $this->_create($interaction), "children" => !empty($filteredArray) ? $this->_buildMenuAndActions($filteredArray) : array());
        }
    }
    return $myMenu;
}

Example output:

Array
 (
     [0] => Array
         (
             [link] => '#address-modal'
             [children] => Array
                 (
                     [0] => Array
                         (
                             [link] => '#mytarget'
                             [children] => Array
                                 (
                                     [0] => Array
                                         (
                                             [link] => '#mytarget3'
                                             [children] => Array
                                                 (
                                                 )

                                         )

                                 )

                         )

                     [1] => Array
                         (
                             [link] => '#mytarget2'
                             [children] => Array
                                 (
                                 )

                         )

                 )

         )

     [1] => Array
         (
             [link] => '#error-modal'
             [children] => Array
                 (
                 )

         )

     [2] => Array
         (
             [link] => '#message-modal'
             [children] => Array
                 (
                 )

         )

     [3] => Array
         (
             [link] => '#user-modal'
             [children] => Array
                 (
                 )

         )

     [4] => Array
         (
             [link] => '#userImage-modal'
             [children] => Array
                 (
                 )

         )

     [5] => Array
         (
             [link] => '#wait-modal'
             [children] => Array
                 (
                 )

         )

 )

So, ah. Any ideas for simplifying this?

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I may be wrong, but I feel as if there would be some much easier (and faster!?) way of doing this with SQL? Assuming you have a sensible DBMS and table structure. Is writing a different query not an option here? \$\endgroup\$ Commented Mar 1, 2016 at 22:57
  • \$\begingroup\$ Added my mysqli query. \$\endgroup\$ Commented Mar 1, 2016 at 23:04
  • 1
    \$\begingroup\$ @SirPython. Thanks, I had some help. Community edit, "request" for sql info, etc. Very helpful community. \$\endgroup\$ Commented Mar 1, 2016 at 23:13

1 Answer 1

1
\$\begingroup\$

Yes, you can simplify your code, since it has three blocks almost identical. The only differences are:

  • $filteredArray comes from either $interactions or $myList.
  • $myMenu[]['children'] is built using either $interactions, $myList, or an empty array.

So instead of working three times depending on three if/else branchs, you can factorize the working part and call it after having defined the needed values.
Not only it ends with a reduced code but also a more readable one, where the logic part is clearly separated.

Here is a suggested version using the above changes, where I also slightly changed the expression of the !empty($myList) condition, for readability and DRYness:

private function _buildMenuAndActions($interactions, $myList = []) {
  $myMenu = [];
  foreach ($interactions as $interaction) {
    if ($interaction['menuID'] === NULL) {
      // First layers.
      $this->_doBuildMAA($interaction, $myMenu, $interactions, $interactions);
    } else {
      if (!empty($myList)) {
        if (array_search($interaction['data_target'], array_column($myList, 'menuID'))) {
          // Layers with nestable layers.
          $this->_doBuildMAA($interaction, $myMenu, $myList, $myList);
        } else {
          // Layers without nestable layers.
          $this->_doBuildMAA($interaction, $myMenu, $interactions, []);
        }
      }
    }
  }
  return $myMenu;
}
private function _doBuildMAA($interaction, &$myMenu, $filterFrom, $buildFrom) {
  $searchFor = $interaction['data_target'];
  $filteredArray = array_filter($filterFrom, function($element) use ($searchFor) {
    return isset($element['menuID']) && $element['menuID'] == $searchFor;
  });
  $myMenu[] = [
    "link" => $this->_create($interaction),
    "children" => !empty($filteredArray) ?
      $this->_buildMenuAndActions($filteredArray, $buildFrom) : []
  ];
}
\$\endgroup\$
10
  • \$\begingroup\$ It's not nesting out of the box brah, I'll review and figure out why. I like the suggested edits though. Very nice. I learned a lot from your answer. \$\endgroup\$ Commented Mar 2, 2016 at 3:49
  • \$\begingroup\$ Oh man, I've got to have my four spaces or I'm really bad at reading code. \$\endgroup\$ Commented Mar 2, 2016 at 3:51
  • 1
    \$\begingroup\$ @EricShoberg Huh... you're lucky I came back here now: I should be in bed, since for me it' 5 AM. Don't clearly understand your comments (note: English is not my mother language): what do you mean with "got to have my four spaces"? In the other hand, not understand your proposed edit too: what is this "added parenthesis"? \$\endgroup\$
    – cFreed
    Commented Mar 2, 2016 at 4:09
  • 1
    \$\begingroup\$ @EricShoberg Ok, I just understood about the edit. For anything else, feel free to ask, but note that for now I'm not here any longer. Back tomorrow. \$\endgroup\$
    – cFreed
    Commented Mar 2, 2016 at 4:20
  • 1
    \$\begingroup\$ @EricShoberg Well, now I understand all, including the parenthesis I'd missed. Sorry for that. Regarding the indentation, I know there are a lot of guys which prefer 4 spaces, while strangely it's the opposite for me: I can't think quietly as long as there are too much (at my eyes) spaces and blank lines in the code! It's a bit astonishing to see that we can be so different... Anyway, glad to help! \$\endgroup\$
    – cFreed
    Commented Mar 2, 2016 at 17:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.