Converti l'output degli elementi nav_menu in una matrice multidimensionale ad albero


11

C'è un modo per afferrare le voci del menu di navigazione come un array multidimensionale anziché un array piatto?

Per struttura ad albero intendo qualcosa che preserverebbe la relazione tra elementi figlio e genitore, in questo modo (questo è solo un esempio) ...

array(
  array(
    'post_type' => 'page',
    'post_name' => 'Home',
    'children' => array() 
  ),
  array(
    'post_type' => 'page',
    'post_name' => 'About Us',
    'children' => array(
      array(
        'post_type' => 'page',
        'post_name' => 'Our History',
        'children' => array() 
      )
    ) 
  )
)

C'è una wp_get_nav_menu_items()funzione ma restituisce un array monodimensionale con tutti gli elementi sullo stesso livello, che non è quello che voglio. WordPress include un modo integrato per ottenere un array multidimensionale per le mie voci di menu? In caso contrario, qual è il modo migliore per ottenere wp_get_nav_menu_items()una struttura ad albero in un array multidimensionale in termini di prestazioni?


3
quell'array monodimensionale contiene tutti i dati necessari per costruire un albero se si utilizza una funzione ricorsiva. per ciascuno degli ID delle voci di menu, cerca altre voci di menu con ID corrispondente nel campo padre dell'oggetto, questi saranno i suoi figli.
Milo,

So di poterne ricavare un albero, ma mi chiedevo se ci fosse già una tale opzione in wp.
YemSalat,

Qual è il tuo caso d'uso? La Walkerclasse gestisce automaticamente la profondità delle voci di menu di spostamento ordinate, anche se l'array è piatto.
Matt van Andel,

1
La tua modifica è sbagliata. Ho modificato il titolo indietro (ho cambiato un paio di parole) L'output di nav_items è un array piatto, non è in alcun modo un albero. Il mio caso d'uso è: voglio gli oggetti di navigazione come un albero, quindi posso farli da solo, senza dover usare le astrazioni rotte di WP.
YemSalat

Ho chiarito un po 'la domanda, per rendere più chiaro ciò che voglio.
YemSalat

Risposte:


21

Il problema di costruire un albero da un array piatto è stato risolto qui con questa soluzione ricorsiva, leggermente modificata:

/**
 * Modification of "Build a tree from a flat array in PHP"
 *
 * Authors: @DSkinner, @ImmortalFirefly and @SteveEdson
 *
 * @link https://stackoverflow.com/a/28429487/2078474
 */
function buildTree( array &$elements, $parentId = 0 )
{
    $branch = array();
    foreach ( $elements as &$element )
    {
        if ( $element->menu_item_parent == $parentId )
        {
            $children = buildTree( $elements, $element->ID );
            if ( $children )
                $element->wpse_children = $children;

            $branch[$element->ID] = $element;
            unset( $element );
        }
    }
    return $branch;
}

dove abbiamo aggiunto l' wpse_childrenattributo con prefisso per evitare la collisione del nome.

Ora non ci resta che definire una semplice funzione di supporto:

/**
 * Transform a navigational menu to it's tree structure
 *
 * @uses  buildTree()
 * @uses  wp_get_nav_menu_items()
 *
 * @param  String     $menud_id 
 * @return Array|null $tree 
 */
function wpse_nav_menu_2_tree( $menu_id )
{
    $items = wp_get_nav_menu_items( $menu_id );
    return  $items ? buildTree( $items, 0 ) : null;
}

Ora diventa super facile trasformare un menu di navigazione nella sua struttura ad albero con:

$tree = wpse_nav_menu_2_tree( 'my_menu' );  // <-- Modify this to your needs!
print_r( $tree );

Per JSON, possiamo semplicemente usare:

$json = json_encode( $tree );

Per una versione leggermente diversa, dove abbiamo raccolto a mano gli attributi, controlla la prima revisione di questa risposta qui .

Aggiornamento: Walker Class

Ecco un'idea piuttosto approssimativa di come potremmo provare ad agganciarci alla parte ricorsiva del display_element()metodo della Walkerclasse astratta .

$w = new WPSE_Nav_Menu_Tree;
$args = (object) [ 'items_wrap' => '', 'depth' => 0, 'walker' => $w ];
$items = wp_get_nav_menu_items( 'my_menu' );
walk_nav_menu_tree( $items, $args->depth, $args );
print_r( $w->branch );  

dove WPSE_Nav_Menu_Treeè un'estensione della Walker_Nav_Menuclasse:

class WPSE_Nav_Menu_Tree extends Walker_Nav_Menu
{
   public $branch = [];

   public function display_element($element, &$children, $max_depth, $depth = 0, $args, &$output )
   {
      if( 0 == $depth )
         $this->branch[$element->ID] = $element;

      if ( isset($children[$element->ID] ) )
         $element->wpse_children = $children[$element->ID];

      parent::display_element($element, $children, $max_depth, $depth, $args, $output);
   }
}

Questo potrebbe darci un approccio alternativo se funziona.


grazie, è sempre interessante e divertente vedere diversi approcci alla risoluzione dei problemi: il tuo aspetto è piuttosto interessante +1. @ialocin
birgire,

1
Lo stesso qui, ma sapevamo già chi ha votato :) Esplorare le possibilità è divertente! Il resto spesso è come il lavoro in catena di montaggio, che è ... diciamo che non è divertente.
Nicolai,

Grazie, speravo che ci sarebbe stata una funzione WP "nativa" per questo. Aspetterò un po 'di più per vedere se qualcuno pubblica altre soluzioni, altrimenti questa sarà la risposta scelta.
YemSalat,

Ho aggiornato la risposta con un altro tipo di approccio @YemSalat
birgire

Whoa! Questo mi fa turbinare la mente. Non avevo mai affrontato la classe Walker prima (so che esiste però) Speravo che ci sarebbe stato un modo più performante di farlo con un paio di query SQL, ma davvero non voglio entrare nella struttura del db WP. Per ora preferirei il tuo primo approccio in cui ricorre ciclicamente wp_get_nav_menu_items.
YemSalat,

3

In breve, la funzione muggito crea l'albero degli oggetti inserendo i bambini in una nuova proprietà figlio all'interno dell'oggetto padre.

Codice:

function wpse170033_nav_menu_object_tree( $nav_menu_items_array ) {
    foreach ( $nav_menu_items_array as $key => $value ) {
        $value->children = array();
        $nav_menu_items_array[ $key ] = $value;
    }

    $nav_menu_levels = array();
    $index = 0;
    if ( ! empty( $nav_menu_items_array ) ) do {
        if ( $index == 0 ) {
            foreach ( $nav_menu_items_array as $key => $obj ) {
                if ( $obj->menu_item_parent == 0 ) {
                    $nav_menu_levels[ $index ][] = $obj;
                    unset( $nav_menu_items_array[ $key ] );
                }
            }
        } else {
            foreach ( $nav_menu_items_array as $key => $obj ) {
                if ( in_array( $obj->menu_item_parent, $last_level_ids ) ) {
                    $nav_menu_levels[ $index ][] = $obj;
                    unset( $nav_menu_items_array[ $key ] );
                }
            }
        }
        $last_level_ids = wp_list_pluck( $nav_menu_levels[ $index ], 'db_id' );
        $index++;
    } while ( ! empty( $nav_menu_items_array ) );

    $nav_menu_levels_reverse = array_reverse( $nav_menu_levels );

    $nav_menu_tree_build = array();
    $index = 0;
    if ( ! empty( $nav_menu_levels_reverse ) ) do {
        if ( count( $nav_menu_levels_reverse ) == 1 ) {
            $nav_menu_tree_build = $nav_menu_levels_reverse;
        }
        $current_level = array_shift( $nav_menu_levels_reverse );
        if ( isset( $nav_menu_levels_reverse[ $index ] ) ) {
            $next_level = $nav_menu_levels_reverse[ $index ];
            foreach ( $next_level as $nkey => $nval ) {
                foreach ( $current_level as $ckey => $cval ) {
                    if ( $nval->db_id == $cval->menu_item_parent ) {
                        $nval->children[] = $cval;
                    }
                }
            }
        }
    } while ( ! empty( $nav_menu_levels_reverse ) );

    $nav_menu_object_tree = $nav_menu_tree_build[ 0 ];
    return $nav_menu_object_tree;
}

Uso:

$nav_menu_items = wp_get_nav_menu_items( 'main-menu' );
wpse170033_nav_menu_object_tree( $nav_menu_items );

Produzione:

Array
(
 [0] => WP_Post Object
  (
   [ID] => 51
   [post_author] => 1
   [post_date] => 2015-06-26 21:13:23
   [post_date_gmt] => 2015-06-26 19:13:23
   [post_content] => 
   [post_title] => 
   [post_excerpt] => 
   [post_status] => publish
   [comment_status] => open
   [ping_status] => open
   [post_password] => 
   [post_name] => 51
   [to_ping] => 
   [pinged] => 
   [post_modified] => 2015-07-29 20:55:10
   [post_modified_gmt] => 2015-07-29 18:55:10
   [post_content_filtered] => 
   [post_parent] => 0
   [guid] => http://example.com/?p=51
   [menu_order] => 1
   [post_type] => nav_menu_item
   [post_mime_type] => 
   [comment_count] => 0
   [filter] => raw
   [db_id] => 51
   [menu_item_parent] => 0
   [object_id] => 2
   [object] => page
   [type] => post_type
   [type_label] => Page
   [url] => http://example.com/example-page/
   [title] => Example-Page-1
   [target] => 
   [attr_title] => 
   [description] => 
   [classes] => Array
    (
     [0] => 
    )
   [xfn] => 
   [children] => Array
    (
     [0] => WP_Post Object
      (
       [ID] => 80
       [post_author] => 1
       [post_date] => 2015-06-27 14:03:31
       [post_date_gmt] => 2015-06-27 12:03:31
       [post_content] => 
       [post_title] => 
       [post_excerpt] => 
       [post_status] => publish
       [comment_status] => open
       [ping_status] => open
       [post_password] => 
       [post_name] => 80
       [to_ping] => 
       [pinged] => 
       [post_modified] => 2015-07-29 20:55:10
       [post_modified_gmt] => 2015-07-29 18:55:10
       [post_content_filtered] => 
       [post_parent] => 2
       [guid] => http://example.com/?p=80
       [menu_order] => 2
       [post_type] => nav_menu_item
       [post_mime_type] => 
       [comment_count] => 0
       [filter] => raw
       [db_id] => 80
       [menu_item_parent] => 51
       [object_id] => 69
       [object] => page
       [type] => post_type
       [type_label] => Page
       [url] => http://example.com/example-page/subpage-1/
       [title] => Subpage-1
       [target] => 
       [attr_title] => 
       [description] => 
       [classes] => Array
        (
         [0] => 
        )
       [xfn] => 
       [children] => Array
        (
        )
      )
    )
  )
 [1] => WP_Post Object
  (
   [ID] => 49
   [post_author] => 1
   [post_date] => 2015-06-26 21:13:23
   [post_date_gmt] => 2015-06-26 19:13:23
   [post_content] => 
   [post_title] => 
   [post_excerpt] => 
   [post_status] => publish
   [comment_status] => open
   [ping_status] => open
   [post_password] => 
   [post_name] => 49
   [to_ping] => 
   [pinged] => 
   [post_modified] => 2015-07-29 20:55:10
   [post_modified_gmt] => 2015-07-29 18:55:10
   [post_content_filtered] => 
   [post_parent] => 0
   [guid] => http://example.com/?p=49
   [menu_order] => 3
   [post_type] => nav_menu_item
   [post_mime_type] => 
   [comment_count] => 0
   [filter] => raw
   [db_id] => 49
   [menu_item_parent] => 0
   [object_id] => 41
   [object] => page
   [type] => post_type
   [type_label] => Page
   [url] => http://example.com/example-page-2/
   [title] => Example-Page-2
   [target] => 
   [attr_title] => 
   [description] => 
   [classes] => Array
    (
     [0] => 
    )
   [xfn] => 
   [children] => Array
    (
    )
  )
 [2] => WP_Post Object
  (
   [ID] => 48
   [post_author] => 1
   [post_date] => 2015-06-26 21:13:23
   [post_date_gmt] => 2015-06-26 19:13:23
   [post_content] => 
   [post_title] => 
   [post_excerpt] => 
   [post_status] => publish
   [comment_status] => open
   [ping_status] => open
   [post_password] => 
   [post_name] => 48
   [to_ping] => 
   [pinged] => 
   [post_modified] => 2015-07-29 20:55:10
   [post_modified_gmt] => 2015-07-29 18:55:10
   [post_content_filtered] => 
   [post_parent] => 0
   [guid] => http://example.com/?p=48
   [menu_order] => 4
   [post_type] => nav_menu_item
   [post_mime_type] => 
   [comment_count] => 0
   [filter] => raw
   [db_id] => 48
   [menu_item_parent] => 0
   [object_id] => 42
   [object] => page
   [type] => post_type
   [type_label] => Page
   [url] => http://example.com/example-page-3/
   [title] => Example-Page-3
   [target] => 
   [attr_title] => 
   [description] => 
   [classes] => Array
    (
     [0] => 
    )
   [xfn] => 
   [children] => Array
    (
     [0] => WP_Post Object
      (
       [ID] => 79
       [post_author] => 1
       [post_date] => 2015-06-27 14:03:31
       [post_date_gmt] => 2015-06-27 12:03:31
       [post_content] => 
       [post_title] => 
       [post_excerpt] => 
       [post_status] => publish
       [comment_status] => open
       [ping_status] => open
       [post_password] => 
       [post_name] => 79
       [to_ping] => 
       [pinged] => 
       [post_modified] => 2015-07-29 20:55:10
       [post_modified_gmt] => 2015-07-29 18:55:10
       [post_content_filtered] => 
       [post_parent] => 42
       [guid] => http://example.com/?p=79
       [menu_order] => 5
       [post_type] => nav_menu_item
       [post_mime_type] => 
       [comment_count] => 0
       [filter] => raw
       [db_id] => 79
       [menu_item_parent] => 48
       [object_id] => 70
       [object] => page
       [type] => post_type
       [type_label] => Page
       [url] => http://example.com/example-page-3/subpage-2/
       [title] => Subpage-2
       [target] => 
       [attr_title] => 
       [description] => 
       [classes] => Array
        (
         [0] => 
        )
       [xfn] => 
       [children] => Array
        (
         [0] => WP_Post Object
          (
           [ID] => 78
           [post_author] => 1
           [post_date] => 2015-06-27 14:03:31
           [post_date_gmt] => 2015-06-27 12:03:31
           [post_content] => 
           [post_title] => 
           [post_excerpt] => 
           [post_status] => publish
           [comment_status] => open
           [ping_status] => open
           [post_password] => 
           [post_name] => 78
           [to_ping] => 
           [pinged] => 
           [post_modified] => 2015-07-29 20:55:10
           [post_modified_gmt] => 2015-07-29 18:55:10
           [post_content_filtered] => 
           [post_parent] => 70
           [guid] => http://example.com/?p=78
           [menu_order] => 6
           [post_type] => nav_menu_item
           [post_mime_type] => 
           [comment_count] => 0
           [filter] => raw
           [db_id] => 78
           [menu_item_parent] => 79
           [object_id] => 76
           [object] => page
           [type] => post_type
           [type_label] => Page
           [url] => http://example.com/example-page-3/subpage-2/subpage-3/
           [title] => Subpage-3
           [target] => 
           [attr_title] => 
           [description] => 
           [classes] => Array
            (
             [0] => 
            )
           [xfn] => 
           [children] => Array
            (
             [0] => WP_Post Object
              (
               [ID] => 87
               [post_author] => 1
               [post_date] => 2015-06-27 15:27:08
               [post_date_gmt] => 2015-06-27 13:27:08
               [post_content] => 
               [post_title] => 
               [post_excerpt] => 
               [post_status] => publish
               [comment_status] => open
               [ping_status] => open
               [post_password] => 
               [post_name] => 87
               [to_ping] => 
               [pinged] => 
               [post_modified] => 2015-07-29 20:55:10
               [post_modified_gmt] => 2015-07-29 18:55:10
               [post_content_filtered] => 
               [post_parent] => 76
               [guid] => http://example.com/?p=87
               [menu_order] => 7
               [post_type] => nav_menu_item
               [post_mime_type] => 
               [comment_count] => 0
               [filter] => raw
               [db_id] => 87
               [menu_item_parent] => 78
               [object_id] => 85
               [object] => page
               [type] => post_type
               [type_label] => Page
               [url] => http://example.com/example-page-3/subpage-2/subpage-3/subpage-4/
               [title] => Subpage-4
               [target] => 
               [attr_title] => 
               [description] => 
               [classes] => Array
                (
                 [0] => 
                )
               [xfn] => 
               [children] => Array
                (
                 [0] => WP_Post Object
                  (
                   [ID] => 366
                   [post_author] => 1
                   [post_date] => 2015-07-29 20:52:46
                   [post_date_gmt] => 2015-07-29 18:52:46
                   [post_content] => 
                   [post_title] => 
                   [post_excerpt] => 
                   [post_status] => publish
                   [comment_status] => open
                   [ping_status] => open
                   [post_password] => 
                   [post_name] => 366
                   [to_ping] => 
                   [pinged] => 
                   [post_modified] => 2015-07-29 20:55:10
                   [post_modified_gmt] => 2015-07-29 18:55:10
                   [post_content_filtered] => 
                   [post_parent] => 85
                   [guid] => http://example.com/?p=366
                   [menu_order] => 8
                   [post_type] => nav_menu_item
                   [post_mime_type] => 
                   [comment_count] => 0
                   [filter] => raw
                   [db_id] => 366
                   [menu_item_parent] => 87
                   [object_id] => 112
                   [object] => page
                   [type] => post_type
                   [type_label] => Page
                   [url] => http://example.com/example-page-3/subpage-2/subpage-3/subpage-4/subpage-5/
                   [title] => Subpage-5
                   [target] => 
                   [attr_title] => 
                   [description] => 
                   [classes] => Array
                    (
                     [0] => 
                    )
                   [xfn] => 
                   [children] => Array
                    (
                    )
                  )
                )
              )
            )
          )
        )
      )
    )
  )
)

Una struttura ad albero in WordPress non è un array multidimensionale. È una matrice di oggetti con informazioni sulla parentela.
Matt van Andel,

Ho provato circa 10 diverse soluzioni per questo problema. Grazie per questa ottima soluzione, la mantiene in una bella struttura di oggetti WP. Questo deve essere effettivamente accettato!
Drmzindec,

@JohanPretorius Grazie e piacere. Bene, le persone sono alla ricerca di cose diverse. Presumo che l'OP abbia trovato più utile l'altra risposta. Va tutto bene.
Nicolai,

1

Versione modificata della risposta accettata in cui prende in considerazione la menu_orderproprietà delle voci di menu al fine di mantenere l'ordine corretto dell'array piatto originale. menu_orderviene impostato automaticamente da WordPress, quindi non è necessario controllare nulla:

function buildTree(array &$flatNav, $parentId = 0) {
    $branch = [];

    foreach ($flatNav as &$navItem) {
      if($navItem->menu_item_parent == $parentId) {
        $children = buildTree($flatNav, $navItem->ID);
        if($children) {
          $navItem->children = $children;
        }

        $branch[$navItem->menu_order] = $navItem;
        unset($navItem);
      }
    }

    return $branch;
}

Uso:

// get navs
$locations = get_nav_menu_locations();

// get menu items by menu name
$flatMainNav = wp_get_nav_menu_items($locations['main']);
$mainNav = buildTree($flatMainNav);

-2

Potrebbe esserci un malinteso qui sulle voci del menu di navigazione di WordPress come strutture ad albero.

Le strutture ad albero in WordPress NON SONO ARRAY MULTIDIMENSIONALI!

Si noti che mentre l'array delle voci di menu restituite è piatto, è comunque una struttura ad albero perché ogni elemento contiene informazioni sulla sua parentela (il valore padre è 0 se l'elemento non ha padre - o l'id dell'elemento padre se esso lo fa).

Quando si passa un array di questo tipo alla Walkerclasse, passa in rassegna i risultati e crea due array: uno contenente elementi di livello superiore e un altro contenente elementi figlio nel formato in $parent_id => array()cui l'array contiene voci di menu che sono dirette figlie di quell'elemento.

Il walker quindi passa in rassegna l'array di elementi di livello superiore, elaborando quell'elemento e quindi controllando l'array figlio per vedere se ci sono elementi figlio per l'elemento corrente ed elabora ciascuno di essi allo stesso modo, in modo ricorsivo.

Se vuoi sapere come convertire una struttura ad albero di WordPress in un array multidimensionale, questa è una domanda completamente diversa (e non tecnicamente una domanda di WordPress). Ma le informazioni restituite da wp_get_nav_menu_items() sono una struttura ad albero ... e puoi usarle così Walkercome sono per gestirle.

Se vuoi vedere il codice esatto che la classe Walker di WordPress esegue per percorrere l'array, dai un'occhiata a Walker-> walk () su WordPress Trac dalle linee 213-258 . Puoi usare quel codice così com'è per costruire un array multidimensionale, se è quello che stai cercando.

Walkers

WordPress è progettato per utilizzare la Walkerclasse per elaborare le sue strutture ad albero. Se stai semplicemente eseguendo il rendering di un menu o hai davvero bisogno dell'output finale, potresti prendere in considerazione l'utilizzo wp_nav_menu()per l'output del tuo menu ...

Esempio:

wp_nav_menu(array(
    'menu' => 6, // your menu id, name, or slug
    'echo' => true, // set this to false if you want a string back instead
    'walker' => new Your_Walker(),
));

Estenderesti la classe Walker (ad es. Your_Walker()) Per ottenere qualsiasi output di cui hai bisogno. Per un esempio, vedere questa voce sul Codice .


2
Nell'opzione A, $sorted_menu_itemsè ancora presente un array "flat" e l'output dell'opzione B è una stringa.
Birgire,

Penso che ci sia un malinteso su come WordPress definisce "strutture ad albero". wp_get_nav_menu_items()restituisce una struttura ad albero, ovvero una matrice in cui ogni elemento contiene dati di parentela. Queste strutture sono pensate per il rendering con una Walkerclasse. Se il caso d'uso qui implica semplicemente la conversione di una matrice "piatta" in una matrice multidimensionale basata su dati di parentela (ad esempio 'post_parent' => 123), questa domanda non riguarda tecnicamente WordPress e dovrebbe essere spostata in Stack Overflow.
Matt van Andel,

1
Senti, non mi interessa ciò che WordPress definisce "strutture ad albero" (non penso che questo sentimento abbia nemmeno senso) Tutto quello che mi interessa è avere un array multidimensionale, con il quale posso fare cose da solo.
YemSalat

NON lo otterrai come comportamento predefinito di WordPress. Come altri hanno già detto, hai tutte le informazioni necessarie per ristrutturare l'array come desideri e ti ho collegato a specifiche aree del core di WordPress da utilizzare come riferimento. Questa non è davvero una domanda WordPress tanto quanto una PHP. Puoi usare la classe Walker così com'è, oppure puoi copiare le righe pertinenti da Walker :: walk () come ho detto, per costruire il tuo array.
Matt van Andel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.