dsfsw1233 2016-08-11 13:43
浏览 119
已采纳

PHP递归菜单多维数组生成器

I'm struggling a bit with some of my code...I have an array of my menu layout:

$navigationMenu = [
    SECTION_OVERVIEW => [
        'hide' => TRUE,
        'id' => 'overview_button',
        'order' => 0,
        'content/overview.php' => [

            SECTION_CABINET => [
                'hide' => TRUE,
                'id' => 'cabinet_button',
                'content/cabinet.php' => [

                    SECTION_AMP_PSU => [
                        'hide' => TRUE,
                        'id' => 'amps_psu_button',
                        'ampNoIndicator' => TRUE,
                        'order' => 3,
                        'content/amp_psu.php' => [

                            SECTION_AMPS_OFF => [
                                'hide' => false,
                                'id' => 'amps_off_button',
                                'ampNoIndicator' => TRUE,
                                'class' => 'red',
                                'order' => 4,
                            ],

                            SECTION_AMPS_ALARMS => [
                                'id' => 'amps_alarms',
                                'ampNoIndicator' => TRUE,
                                'order' => 5,
                                'content/amp_alarms.php' => NULL
                            ]
                        ]
                    ],
                    'POOP' => [
                        'hide' => false,
                        'id' => 'amps_psu_button',
                        'ampNoIndicator' => TRUE,
                        'order' => 3,
                        'content/amp_psu.php' => NULL
                    ]
                ]
            ]
        ]
    ],
    SECTION_SYSCONFIG => [
        'id' => 'sysConfig',
        'order' => 1,
        'content/system_configuration.php' => NULL,
    ],

    SECTION_SYS => [
        'id' => 'system',
        'order' => 2,
        'content/system.php' => NULL,
    ],

    BACK_BUTTON => [
        'id' => 'backBtn',
        'order' => 100,
        'prev' => NULL,
    ],

    RESCAN_BUTTON => [
        'id' => 'rescan',
        'order' => 101,
    ]
];

And I have a class that generates the menu.

class Navigation {
    private $doc;
    private $xpath;
    private $rootNode;

    /**
     *  Initialize navigation.
     */
    public function __construct() {
        global $navigationMenu;
        $menu = $navigationMenu;
        $this->doc = new DOMDocument();
        $this->doc->appendChild($this->buildMenu($menu));
        $this->xpath = new DOMXpath($this->doc);
    }

    /**
     *  Build Menu
     *  @param  $menu       array       see $menu array at the begining of the class
     *  @param  $depth      int         maintain a depth count for nested UL menu
     *  @param  $nestingUL  bool        false (default): does not output menu in a nested UL config
     *                                  true: nested UL config
     *  
     *  @return DOMElement              function returns DOM element of UL
     */
    private function buildMenu($menu, $depth = 0, $nestingUL = false) {
        $ulNode = $this->doc->createElement('ul');

        if (!$depth) {
            $ulNode->setAttribute('id', 'primary-nav');
            $this->rootNode = $ulNode;
        }

        $this->appendtoNode($menu, $depth, $nestingUL, $ulNode);

        return $ulNode;
    }

    /**
     *  Append menu items to a node (either UL or LI)
     *  
     *  @param  $menu                   array       array of menu items list
     *  @param  $depth                  array       depth count for nested UL menu
     *  @param  $nestingUL              bool        false: no nesting UL; true: nesting UL
     *  @param  $node                   DOMElement  passing node variable
     *  @param  $showElementOverride    bool        override skipping hidden elements
     *  
     */
    private function appendtoNode($menu, $depth, $nestingUL, $node, $showElementOverride = false) {
        foreach ($menu as $itemText => $item) {
            if ((empty($item['hide']) || !($item['hide']) || $showElementOverride)) {
                $node->appendChild($this->buildMenuItem($itemText, $item, $depth, $nestingUL));
            }

            else if (array_key_exists('hide', $item) && $item['hide']) {
                $newArr = $this->hiddenArraySkipNext($menu);
                $this->appendtoNode($newArr, $depth, $nestingUL, $node, $showElementOverride);
            }
        }
        return;
    }

  /**
   *  Build menu item.
   *  
   *  @param   $itemText        string          (button/menu label)
   *  @param   $item            array           (button modifiers array)
   *  @param   $depth           int             (maintaining depth count (only for creating a nested UL))
   *  @param   $nesting         bool            (true if nesting is required; false if no nesting)
   */  
    private function buildMenuItem($itemText, $item, $depth, $nesting) {
        $id = '';
        $spanclassAttr = array('navButton-color');
        $order = '';
        $url = '';
        $ampNo = '';
        $childMenu = false;

        // prepare node structure
           $liNode = $this->doc->createElement('li');
         $spanNode = $this->doc->createElement('span');
        $glareNode = $this->doc->createElement('span');     // spare span tag used to carry the glare class attribute
            $pNode = $this->doc->createElement('p');

        // initialize node attributes
        $liNode->setAttribute('class', 'navButton');
        $glareNode->setAttribute('class', 'glare');         // spare span tag with "glare" class attribute

        // iterate item properties
        foreach ($item as $key => $value) {
            switch ($key) {
                case 'hide':
                    break;
                case 'id':
                    $id = $value;
                    break;
                case 'ampNoIndicator':
                    $ampNo = $value;
                    break;
                case 'class':
                    $spanclassAttr[] = $value;
                    break;
                case 'order':
                    $order = $value;
                    break;
                default:
                    $url = $key;
                    $childMenu = $value;
            }
        }

        // map iterated items to nodes
        $liNode->setAttribute('id', $id);

        if ($spanclassAttr) {
            $spanNode->setAttribute('class', implode(' ', $spanclassAttr));
        }

        $pNode->nodeValue = $itemText;
        $spanNode->appendChild($pNode);
        $liNode->appendChild($spanNode);
        $liNode->appendChild($glareNode);

        if (is_array($childMenu) && $nesting) {
            $liNode->appendChild($this->buildMenu($childMenu, $depth + 1, $nesting));
        }
        else if (is_array($childMenu) && !$nesting) {
            $this->appendtoNode($childMenu, $depth, $nesting, $liNode);
        }
        return $liNode;
    }

    /**
     *  Iterating menu array
     *  
     *  @param  $item           menu array
     *  @return array | bool    return the nested array, else return false
     */
    private function hiddenArraySkipNext($arr) {
        $output = $arr;
        foreach ($arr as $tempArr) {
            if (array_key_exists('hide', $tempArr) && $tempArr['hide']) {
                foreach ($tempArr as $key => $value) {
                    if (is_array($value)) {
                        $output = $value;
                        $this->hiddenArraySkipNext($output);
                    }
                }
            }
            else if (!array_key_exists('hide', $tempArr) || (array_key_exists('hide', $tempArr) && !$tempArr['hide'])) {
                return $output;
            }
        }
        return $output;
    }

    /**
    * Get menu.
    */
    public function getMenu() {
        return $this->doc->saveHTML();
    }
}

So the purpose of the class is to be able to generate either a nested or non-nested UL of the $navigationMenu (see the use of the $nestingUL/$nesting parameters I have through buildMenu, appendtoNode, etc). I also want to have the ability to override any "hide" keys in my $navigationMenu (see $showElementOverride in the appendtoNode() function). The menu displays as planned when the array is in the configuration I have set up. However, if I ever wanted to expand on the menu, I run into issues.

In this array configuration, below, my SECTION_AMPS_OFF and SECTION_AMPS_ALARMS are output twice.

$navigationMenu = [
    SECTION_OVERVIEW => [
        'hide' => TRUE,
        'id' => 'overview_button',
        'order' => 0,
        'content/overview.php' => [

            SECTION_CABINET => [
                'hide' => TRUE,
                'id' => 'cabinet_button',
                'content/cabinet.php' => [

                    SECTION_AMP_PSU => [
                        'hide' => TRUE,
                        'id' => 'amps_psu_button',
                        'ampNoIndicator' => TRUE,
                        'order' => 3,
                        'content/amp_psu.php' => [

                            SECTION_AMPS_OFF => [
                                'hide' => false,
                                'id' => 'amps_off_button',
                                'ampNoIndicator' => TRUE,
                                'class' => 'red',
                                'order' => 4,
                            ],

                            SECTION_AMPS_ALARMS => [
                                'id' => 'amps_alarms',
                                'ampNoIndicator' => TRUE,
                                'order' => 5,
                                'content/amp_alarms.php' => NULL
                            ]
                        ]
                    ],
                    'POOP' => [
                        'hide' => true,
                        'id' => 'amps_psu_button',
                        'ampNoIndicator' => TRUE,
                        'order' => 3,
                        'content/amp_psu.php' => NULL
                    ]
                ]
            ]
        ]
    ],
    SECTION_SYSCONFIG => [
        'id' => 'sysConfig',
        'order' => 1,
        'content/system_configuration.php' => NULL,
    ],

    SECTION_SYS => [
        'id' => 'system',
        'order' => 2,
        'content/system.php' => NULL,
    ],

    BACK_BUTTON => [
        'id' => 'backBtn',
        'order' => 100,
        'prev' => NULL,
    ],

    RESCAN_BUTTON => [
        'id' => 'rescan',
        'order' => 101,
    ]
];

And when I have the menu configured as below, I get a fatal error about the allowed memory size exhausted.

$navigationMenu = [
    SECTION_OVERVIEW => [
        'hide' => TRUE,
        'id' => 'overview_button',
        'order' => 0,
        'content/overview.php' => [

            SECTION_CABINET => [
                'hide' => TRUE,
                'id' => 'cabinet_button',
                'content/cabinet.php' => [

                    SECTION_AMP_PSU => [
                        'hide' => TRUE,
                        'id' => 'amps_psu_button',
                        'ampNoIndicator' => TRUE,
                        'order' => 3,
                        'content/amp_psu.php' => [

                            SECTION_AMPS_OFF => [
                                'hide' => true,
                                'id' => 'amps_off_button',
                                'ampNoIndicator' => TRUE,
                                'class' => 'red',
                                'order' => 4,
                            ],

                            SECTION_AMPS_ALARMS => [
                                'id' => 'amps_alarms',
                                'ampNoIndicator' => TRUE,
                                'order' => 5,
                                'content/amp_alarms.php' => NULL
                            ]
                        ]
                    ],
                    'POOP' => [
                        'hide' => false,
                        'id' => 'amps_psu_button',
                        'ampNoIndicator' => TRUE,
                        'order' => 3,
                        'content/amp_psu.php' => NULL
                    ]
                ]
            ]
        ]
    ],
    SECTION_SYSCONFIG => [
        'id' => 'sysConfig',
        'order' => 1,
        'content/system_configuration.php' => NULL,
    ],

    SECTION_SYS => [
        'id' => 'system',
        'order' => 2,
        'content/system.php' => NULL,
    ],

    BACK_BUTTON => [
        'id' => 'backBtn',
        'order' => 100,
        'prev' => NULL,
    ],

    RESCAN_BUTTON => [
        'id' => 'rescan',
        'order' => 101,
    ]
];

I'm pretty sure the solution is not expanding the memory limit. The ultimate goal is to be able to loop through the array and spit out any unhidden nav menu buttons (either nested or non-nested UL), as long as $showElementOverride is not set TRUE. If a parent array is hidden, the child may be shown, as long as it's not a nested UL. I haven't tackled that issue yet, as I've been trying to tackle the hiddenArraySkipNext() function. I've been struggling with this for the past couple of days, so a fresh set of eyes would definitely be appreciated!

  • 写回答

1条回答 默认 最新

  • dongtiao5094 2016-08-11 21:49
    关注

    Rewrote my buildMenu(), appendtoNode() and hiddenArraySkipNext() functions:

    private function buildMenu($menu, $depth = 0, $nestingUL = true, $showElementOverride = false) {
        $ulNode = $this->doc->createElement('ul');
    
        if (!$depth) {
            $ulNode->setAttribute('id', 'primary-nav');
            $this->rootNode = $ulNode;
        }
    
        $this->appendtoNode($menu, $depth, $nestingUL, $ulNode, $showElementOverride);
    
        return $ulNode;
    }
    
    private function appendtoNode($menu, $depth, $nestingUL, $node, $showElementOverride = false) {
        foreach ($menu as $itemText => $item) {
            if (empty($item['hide']) || !($item['hide']) || $showElementOverride) {
                $node->appendChild($this->buildMenuItem($itemText, $item, $depth, $nestingUL));
            }
    
            else if ((array_key_exists('hide', $item) && $item['hide']) && !$nestingUL) {
                $tempMenu = $this->hiddenArraySkipNext($item);
    
                if (is_array($tempMenu)) $this->appendtoNode($tempMenu, $depth, $nestingUL, $node, $showElementOverride);
            }
        }
        return;
    }
    
    private function hiddenArraySkipNext($arr = array()) {
        $output = array();
        foreach ($arr as $tempArr) {
            $output = $tempArr;
            if (is_array($tempArr) && !empty($tempArr)) {
                foreach ($tempArr as $val) {
                    if (is_array($val) && array_key_exists('hide', $val) && $val['hide']) {
                        $this->hiddenArraySkipNext($val);   
                    }
                }
            }
        }
        return $output;
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 乌班图ip地址配置及远程SSH
  • ¥15 怎么让点阵屏显示静态爱心,用keiluVision5写出让点阵屏显示静态爱心的代码,越快越好
  • ¥15 PSPICE制作一个加法器
  • ¥15 javaweb项目无法正常跳转
  • ¥15 VMBox虚拟机无法访问
  • ¥15 skd显示找不到头文件
  • ¥15 机器视觉中图片中长度与真实长度的关系
  • ¥15 fastreport table 怎么只让每页的最下面和最顶部有横线
  • ¥15 java 的protected权限 ,问题在注释里
  • ¥15 这个是哪里有问题啊?