2016-08-11 18 views
0

私は私のコードの一部で少し苦労してる...私は私のメニューレイアウトの配列を持っている:PHP再帰的なメニュー多次元配列ジェネレータ

$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が2回出力されます。

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

解決策がメモリの制限を拡大していないことは間違いありません。最終的な目標は、配列をループして、$ showElementOverrideがTRUEに設定されていない限り、非表示のナビゲーションメニューボタン(ネストされたULまたはネストされていないUL)を吐き出すことです。親配列が隠されている場合、その子はネストされたULでない限り表示されます。私はhiddenArraySkipNext()関数に取り組もうとしているので、まだこの問題に取り組んでいません。私は過去2〜3日間これに苦しんでいたので、目の新鮮なセットは間違いなく評価されるでしょう!

+0

あなたがメモリ不足に行けば、チャンスはある、あなたの再帰は終了しません。 – Jakumi

+0

ええ、私は多くを考えました。 hiddenArraySkipNext()関数内の私の2番目のforeachループは、その関数が私のnavメニュー配列のSECTION_AMPS_OFFネストされた配列にループするときに強制的に実行されるので、その理由がわかります。 – JayZiggaZeus

答えて

0

書き直し私buildMenu()、appendtoNode()とhiddenArraySkipNext()関数は、:

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