2017-06-27 6 views
6

Ich mache ein verschachteltes Menü Array aus der Antwort, die ich von WP-Datenbank erhalten. Ich bekomme die Daten von WP in der Steuerung in Laravel mit Hilfe von corcel package, und mache dann ein Array mit Menü-Daten, die jetzt eine Ebene tief ist. Also, wenn ein Menü-Link einen Untermenü Link hat, sieht das Array wie folgt:PHP - Erstellen einer verschachtelten Struktur Menüstruktur aus einem flachen Array

{ 
    "Hjem": { 
     "ID": 112, 
     "title": "Hjem", 
     "slug": "hjem", 
     "url": "http://hivnorge.app/?p=112", 
     "status": "publish", 
     "main_category": "Hovedmeny", 
     "submenus": [ 
      { 
       "ID": 129, 
       "title": "Lorem ipsum", 
       "slug": "lorem-ipsum", 
       "url": "http://hivnorge.app/?p=129", 
       "status": "publish", 
       "main_category": "Nyheter" 
      } 
     ] 
    }, 
    "Nytt test innlegg": { 
     "ID": 127, 
     "title": "Nytt test innlegg", 
     "slug": "nytt-test-innlegg", 
     "url": "http://hivnorge.app/?p=127", 
     "status": "private", 
     "main_category": "Nyheter", 
     "submenus": [ 
      { 
       "ID": 125, 
       "title": "Test innlegg", 
       "slug": "test-innlegg", 
       "url": "http://hivnorge.app/?p=125", 
       "status": "publish", 
       "main_category": "Nyheter" 
      }, 
      { 
       "ID": 129, 
       "title": "Lorem ipsum", 
       "slug": "lorem-ipsum", 
       "url": "http://hivnorge.app/?p=129", 
       "status": "publish", 
       "main_category": "Nyheter" 
      } 
     ] 
    }, 
    "Prosjektsamarbeidets verdi": { 
     "ID": 106, 
     "title": "Prosjektsamarbeidets verdi", 
     "slug": "prosjektsamarbeidets-verdi", 
     "url": "http://hivnorge.no.wordpress.seven.fredrikst/?p=106", 
     "status": "publish", 
     "main_category": "Prevensjon" 
    } 
} 

Dies ist, wie ich diese Antwort erschaffe:

 $menu = Menu::slug('hovedmeny')->first(); 
     $res = []; 

     foreach ($menu->nav_items as $item) { 
      $item->makeHidden($hiddenAttributes)->toArray(); 
      $parent_id = $item->meta->_menu_item_menu_item_parent; 

      if ($parent_id == '0') { 
       if ($item->title == '') { 
        $item = $this->findPost($item); 
       } 
       $parentItem = $item; 
       $res[$parentItem->title] = $parentItem->makeHidden($hiddenAttributes)->toArray(); 
      } 
      else { 
       $childItem = $this->findPost($item); 
       $res[$parentItem->title]['submenus'][] = $childItem->makeHidden($hiddenAttributes)->toArray(); 
      } 
     } 

     return $res; 

Das Problem, das ich habe ist, dass die Antwort von WP Findet nur parent_id für jede $item und keine Daten zu, wenn ein Artikel ein paar Kinder hat, so ist dies die Metadaten des übergeordneten Artikels zum Beispiel:

  #attributes: array:4 [ 
      "meta_id" => 209 
      "post_id" => 112 
      "meta_key" => "_menu_item_menu_item_parent" 
      "meta_value" => "0" 
      ] 

und das ist th e Meta-Daten des Kindes Artikel:

  #attributes: array:4 [ 
      "meta_id" => 326 
      "post_id" => 135 
      "meta_key" => "_menu_item_menu_item_parent" 
      "meta_value" => "112" 
      ] 

Wie kann ich diese flexibler zu machen und ermöglichen tiefer nisten, so dass ich Untermenüs innerhalb Untermenüs haben kann?

Ich habe versucht, nach der Lösung here zu suchen, denn das ist so ziemlich das gleiche Problem wie meins, aber war nicht in der Lage, es zu implementieren. In meinem Array Menü Elemente haben auch nur parent_id, und die parent_id das ist 0 wird als ein Wurzelelement betrachtet. Auch die parent_id wenn die menu item ist eine post, verweist auf die meta id, und nicht die ID der post, die ich brauche, also muss ich das zusätzlich von meta->_menu_item_object_id bekommen.

UPDATE

Ich habe es geschaffen, einen Baum wie Struktur zu machen, aber das Problem, das ich jetzt habe, dass ich weiß nicht, wie die title für die Menüelemente zu erhalten, die posts sind. Ich habe das im vorherigen Beispiel durch Prüfen, ob die title leer ist, dann würde ich für diese post von id suchen:

  if ($item->title == '') { 
       $item = $this->findPost($item); 
      } 

Aber mit dem neuen Code, wo ich einen Baum wie Struktur mache ich bin nicht sicher, wie das geht, da ich dann nicht in der Lage bin, die Baumstruktur zu erstellen, da ich alles mit der id vergleiche und die ids des Menüelements sich von der id der post unterscheidet, die darauf hinweist, also dann bin ich nicht in der Lage, die Baumstruktur zu machen:

private function menuBuilder($menuItems, $parentId = 0) 
    { 
     $hiddenAttributes = \Config::get('middleton.wp.menuHiddenAttributes'); 
     $res = []; 

     foreach ($menuItems as $index => $item) { 
      $itemParentId = $item->meta->_menu_item_menu_item_parent; 

      if ($itemParentId == $parentId) { 
       $children = self::menuBuilder($menuItems, $item->ID); 

       if ($children) { 
        $item['submenu'] = $children; 
       } 

       $res[$item->ID] = $item->makeHidden($hiddenAttributes)->toArray(); 
       unset($menuItems[$index]); 
      } 
     } 

     return $res; 
    } 

Also, dann die Daten, die ich bekommen ist:

{ 
    "112": { 
     "ID": 112, 
     "submenu": { 
      "135": { 
       "ID": 135, 
       "title": "", 
       "slug": "135", 
       "url": "http://hivnorge.app/?p=135", 
       "status": "publish", 
       "main_category": "Hovedmeny" 
      } 
     }, 
     "title": "Hjem", 
     "slug": "hjem", 
     "url": "http://hivnorge.app/?p=112", 
     "status": "publish", 
     "main_category": "Hovedmeny" 
    }, 
    "136": { 
     "ID": 136, 
     "submenu": { 
      "137": { 
       "ID": 137, 
       "submenu": { 
        "138": { 
         "ID": 138, 
         "title": "", 
         "slug": "138", 
         "url": "http://hivnorge.app/?p=138", 
         "status": "publish", 
         "main_category": "Hovedmeny" 
        } 
       }, 
       "title": "", 
       "slug": "137", 
       "url": "http://hivnorge.app/?p=137", 
       "status": "publish", 
       "main_category": "Hovedmeny" 
      } 
     }, 
     "title": "", 
     "slug": "136", 
     "url": "http://hivnorge.app/?p=136", 
     "status": "publish", 
     "main_category": "Hovedmeny" 
    }, 
    "139": { 
     "ID": 139, 
     "title": "", 
     "slug": "139", 
     "url": "http://hivnorge.app/?p=139", 
     "status": "publish", 
     "main_category": "Hovedmeny" 
    } 
} 

Antwort

1

Eine Möglichkeit, schreiben müssen, um zu sehen, dieses Problem zu lösen machen Verwendung von variablen Aliasen.Wenn Sie darauf achten, eine Nachschlagetabelle (Array) für die IDs zu verwalten, können Sie sie verwenden, um sie an der richtigen Stelle des hierarchischen Menüarrays einzufügen, da verschiedene Variablen (hier Arrayeinträge in der Nachschlagetabelle) auf denselben Wert verweisen können .

Im folgenden Beispiel wird dies demonstriert. Es löst auch das zweite Problem (implizit in Ihrer Frage), dass das flache Array nicht sortiert ist (die Reihenfolge ist in einer Datenbank-Ergebnistabelle undefiniert), daher kann ein Untermenüeintrag im Resultset vor dem Menüeintrag den Untermenüeintrag gehört zu.

Zum Beispiel habe ich eine einfache flache Array:

# some example rows as the flat array 
$rows = [ 
    ['id' => 3, 'parent_id' => 2, 'name' => 'Subcategory A'], 
    ['id' => 1, 'parent_id' => null, 'name' => 'Home'], 
    ['id' => 2, 'parent_id' => null, 'name' => 'Categories'], 
    ['id' => 4, 'parent_id' => 2, 'name' => 'Subcategory B'], 
]; 

Dann für die Arbeit schleppen Hauptvariablen zu tun gibt es: Zuerst wird die $menu, die die hierarchische Anordnung ist zu erstellen und zweiten $byId das ist die Lookup Tabelle:

# initialize the menu structure 
$menu = []; # the menu structure 
$byId = []; # menu ID-table (temporary) 

Die Nachschlagetabelle ist nur so lange notwendig, wie das Menü aufgebaut ist, es wird danach weggeworfen.

Der nächste große Schritt ist das Erstellen der $menu durch das Traversieren über das flache Array. Dies ist eine größere foreach Schleife:

# build the menu (hierarchy) from flat $rows traversable 
foreach ($rows as $row) { 
    # map row to local ID variables 
    $id = $row['id']; 
    $parentId = $row['parent_id']; 

    # build the entry 
    $entry = $row; 
    # init submenus for the entry 
    $entry['submenus'] = &$byId[$id]['submenus']; # [1] 

    # register the entry in the menu structure 
    if (null === $parentId) { 
     # special case that an entry has no parent 
     $menu[] = &$entry; 
    } else { 
     # second special case that an entry has a parent 
     $byId[$parentId]['submenus'][] = &$entry; 
    } 

    # register the entry as well in the menu ID-table 
    $byId[$id] = &$entry; 

    # unset foreach (loop) entry alias 
    unset($entry); 
} 

Dies ist, wo die Einträge aus der flachen Anordnung abgebildet werden ($rows) in die hierarchischen $menu Array. Dank der Stack- und Lookup-Tabelle $byId ist keine Rekursion erforderlich.

Der Schlüsselpunkt hier ist die Verwendung von Variablen Aliase (Referenzen) beim Hinzufügen neuer Einträge in die Struktur $menu sowie beim Hinzufügen von ihnen zu $byId. Dies ermöglicht es den gleichen Wert in Speicher mit zwei verschiedenen Variablennamen für den Zugriff auf:

 # special case that an entry has no parent 
     $menu[] = &$entry; 
     ... 

    # register the entry as well in the menu ID-table 
    $byId[$id] = &$entry; 

Es ist mit der = & Zuordnung getan, und es bedeutet, dass $byId[$id] Zugang zu $menu[<< new key >>] gibt.

Das gleiche im Fall getan wird, wird es in ein Untermenü hinzugefügt:

# second special case that an entry has a parent 
    $byId[$parentId]['submenus'][] = &$entry; 
... 

# register the entry as well in the menu ID-table 
$byId[$id] = &$entry; 

Hier $byId[$id] Punkte $menu...[ << parent id entry in the array >>]['submenus'][ << new key >> ].

Dies löst das Problem, immer den richtigen Ort zu finden, an dem ein neuer Eintrag in die hierarchische Struktur eingefügt werden soll.

mit den Fällen befassen, die ein Untermenü in der flachen Anordnung kommt vor den Menüeintrag zu der es gehört, das Untermenü, wenn für neue Einträge initialisiert muss aus der Lookup-Tabelle entnommen werden (bei [1]):

# init submenus for the entry 
$entry['submenus'] = &$byId[$id]['submenus']; # [1] 

Dies ist ein bisschen ein besonderer Fall. Wenn $byId[$id]['submenus'] noch nicht gesetzt ist (z. B. in der ersten Schleife), wird es aufgrund der Referenz implizit auf null gesetzt (die & vor &$byId[$id]['submenus']). Wenn es gesetzt ist, wird das bestehende Untermenü von einem noch nicht existierenden Eintrag verwendet, um das Untermenü des Eintrags zu initialisieren.

Dies ist genug, um nicht auf eine bestimmte Reihenfolge in $rows abhängen.

Dies ist, was die Schleife tut.

Der Rest ist Aufräumarbeiten:

# unset ID aliases 
unset($byId); 

Es führt zum Löschen der Tabellen-Look-ID als es nicht mehr benötigt wird. Das heißt, alle Aliase sind nicht gesetzt.

das Beispiel vervollständigen:

# visualize the menu structure 
print_r($menu); 

die dann folgende Ausgabe gibt:

Array 
(
    [0] => Array 
     (
      [id] => 1 
      [parent_id] => 
      [name] => Home 
      [submenus] => 
     ) 

    [1] => Array 
     (
      [id] => 2 
      [parent_id] => 
      [name] => Categories 
      [submenus] => Array 
       (
        [0] => Array 
         (
          [id] => 3 
          [parent_id] => 2 
          [name] => Subcategory A 
          [submenus] => 
         ) 

        [1] => Array 
         (
          [id] => 4 
          [parent_id] => 2 
          [name] => Subcategory B 
          [submenus] => 
         ) 

       ) 

     ) 

) 

Ich hoffe, das ist verständlich und Sie sind in der Lage diese auf Ihrem konkreten Szenario anzuwenden. Sie können dies in eine eigene Funktion einfügen (was ich vorschlagen würde), ich habe es nur für das Beispiel ausführlich gehalten, um die Teile besser zu demonstrieren.

Verwandte Q & Material:

+0

Wenn Sie mehr über die Referenzen in PHP lesen möchten, finden Sie im Handbuch eine Anleitung dazu http://php.net/manual/en/language.references.php – hakre

+0

Vielen Dank für Ihre ausführliche Antwort! – Leff

+0

Vielen Dank für diese nette Prämie! Wenn es nützlich ist, denken Sie bitte an eine Verbesserung der Antwort. – hakre

1

So würden Sie eine rekursive Funktion What is a RECURSIVE Function in PHP?

So etwas wie

function menuBuilder($menuItems){ 
    foreach($menuItems as $key => $item) 
    { 
     if(!empty($item->children)){ 
      $output[$key] = menuBuilder($item->children); 
     } 
    } 
    return $output; 
} 
+2

Ich bin nicht sicher, wie das zu tun, da ich nicht aus der Antwort überprüfen oder sehen kann Ich bekomme von WP, wenn das MenuItem Kinder hat, kann ich nur sehen, wenn das Element einen Elternteil hat, und dann bin ich mir nicht sicher, wie man es an der richtigen Stelle im Array platziert. – Leff

Verwandte Themen