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

时间:2016-08-11 13:43:02

标签: php arrays class oop menu

我在使用我的一些代码时遇到了一些麻烦......我有一个菜单布局数组:

$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,
    ]
];

我有一个生成菜单的课程。

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();
    }
}

因此,该类的目的是能够生成$ navigationMenu的嵌套或非嵌套UL(请参阅使用我通过buildMenu,appendtoNode等获得的$ nestingUL / $嵌套参数)。我还希望能够覆盖$ navigationMenu中的任何“隐藏”键(请参阅appendtoNode()函数中的$ showElementOverride)。当阵列处于我设置的配置中时,菜单按计划显示。但是,如果我想扩展菜单,我会遇到问题。

在下面的数组配置中,我的SECTION_AMPS_OFF和SECTION_AMPS_ALARMS输出两次。

$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,
    ]
];

当我将菜单配置如下时,我得到一个关于允许的内存大小耗尽的致命错误。

$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,
    ]
];

我很确定解决方案不会扩展内存限制。最终目标是能够遍历数组并吐出任何未隐藏的导航菜单按钮(嵌套或非嵌套UL),只要$ showElementOverride未设置为TRUE。如果隐藏父数组,则可以显示子项,只要它不是嵌套的UL。我还没有解决这个问题,因为我一直试图解决hiddenArraySkipNext()函数。在过去的几天里,我一直在努力解决这个问题,所以一定要戴上一双新鲜的眼睛!

1 个答案:

答案 0 :(得分:0)

重写了我的buildMenu(),appendtoNode()和hiddenArraySkipNext()函数:

import urllib2
import getpass
import os
import json
import sys
import base64
import traceback

# Set the request parameters
url = 'https://outlook.office.com/api/v1.0/me/events/AAMkADA1OWVjOTkxLTlmYmEtNDAwMS04YWU3LTNkNDE2YjU2OGI1ZABGBBBBBBD_fa49_h8OTJ5eGdjSTEF3BwBOcCSV9aNzSoXurwI4R0IgBBBBBBENAABOcCSV9aNzSoXurwI4R0IgAAHzfZ0mAAA=/attachments'
user = 'user1@domain.com'
pwd = "password123"

# Create JSON payload
data = {
        "@odata.type": "#Microsoft.OutlookServices.FileAttachment",
        "Name": "menu.txt",
        "ContentBytes": "VGVzdCAxMjM0NQ=="
      }


print data

json_payload = json.dumps(data)

# Build the HTTP request
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(url, data=json_payload)
auth = base64.encodestring('%s:%s' % (user, pwd)).replace('\n', '')
request.add_header('Authorization', 'Basic %s' % auth)
request.add_header('Content-Type', 'application/json')
request.add_header('Accept', 'application/json')
request.get_method = lambda: 'POST'
# Perform the request
result = opener.open(request).read()