Convertir la sortie des éléments nav_menu en un tableau multidimensionnel en forme d'arbre

11

Existe-t-il un moyen de saisir les éléments du menu nav en tant que tableau multidimensionnel au lieu d'un tableau plat?

Par une structure arborescente, je veux dire quelque chose qui préserverait la relation entre les éléments enfant et parent, comme ça (ce n'est qu'un exemple)…

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

Il y a une wp_get_nav_menu_items()fonction mais elle renvoie un tableau unidimensionnel avec tous les éléments au même niveau, ce qui n'est pas ce que je veux. WordPress inclut-il une méthode intégrée pour obtenir un tableau multidimensionnel pour mes éléments de menu? Sinon, quelle est la meilleure façon d'intégrer wp_get_nav_menu_items()une structure arborescente dans un tableau multidimensionnel en termes de performances?

YemSalat
la source
3
ce tableau unidimensionnel contient toutes les données dont vous avez besoin pour construire un arbre si vous utilisez une fonction récursive. pour chacun des ID d'élément de menu, recherchez d'autres éléments de menu avec l'ID correspondant dans le champ parent de l'objet, ce seront ses enfants.
Milo
Je sais que je peux en faire un arbre, mais je me demandais s'il y avait déjà une telle option dans wp.
YemSalat
Quel est votre cas d'utilisation? La Walkerclasse gère automatiquement la profondeur des éléments de menu de navigation triés, même si le tableau est plat.
Matt van Andel
1
Votre modification est incorrecte. J'ai édité le titre en arrière (changé quelques mots) La sortie de nav_items est un tableau plat, ce n'est pas un arbre dans tous les sens. Mon cas d'utilisation est - je veux les éléments de navigation sous forme d'arbre, donc je peux faire des minces avec lui-même, sans avoir à utiliser les abstractions cassées de WP.
YemSalat
J'ai clarifié un peu la question, pour préciser ce que je veux.
YemSalat

Réponses:

21

Le problème de la construction d'un arbre à partir d'un tableau plat a été résolu ici avec cette solution récursive légèrement modifiée:

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

où nous avons ajouté l' wpse_childrenattribut préfixé pour éviter la collision de noms.

Il ne nous reste plus qu'à définir une simple fonction d'aide:

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

Maintenant, il devient super facile de transformer un menu de navigation en sa structure arborescente avec:

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

Pour JSON, nous pouvons simplement utiliser:

$json = json_encode( $tree );

Pour une version légèrement différente, où nous avons trié sur le volet les attributs, consultez la première révision de cette réponse ici .

Mise à jour: Walker Class

Voici une idée assez sommaire de la façon dont nous pourrions essayer de nous accrocher à la partie récursive de la display_element()méthode de la Walkerclasse abstraite .

$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 );  

WPSE_Nav_Menu_Treeest une extension de la 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);
   }
}

Cela pourrait nous donner une approche alternative si cela fonctionne.

Birgire
la source
merci, c'est toujours intéressant et amusant de voir différentes approches de résolution de problèmes - votre look est plutôt cool +1. @ialocin
birgire
1
Même chose ici, mais nous savions déjà qui avait voté :) Explorer les possibilités est amusant! Le reste est souvent comme un travail à la chaîne, ce qui est ... disons simplement pas amusant.
Nicolai
Merci, j'espérais qu'il y aurait une fonction WP "native" pour cela. J'attendrai un peu plus pour voir si quelqu'un publie d'autres solutions, sinon ce sera la réponse choisie.
YemSalat
J'ai mis à jour la réponse avec un autre type d'approche @YemSalat
birgire
Whoa! Cela fait tourbillonner mon esprit. Je n'avais jamais traité de la classe Walker auparavant (je sais qu'elle existe cependant) J'espérais qu'il y aurait une manière plus performante de le faire avec quelques requêtes SQL, mais je ne veux vraiment pas entrer dans la structure WP db. Pour l'instant je préférerais votre première approche où son cycle passe wp_get_nav_menu_itemsrécursivement.
YemSalat
3

En bref, la fonction ci-dessous crée l'arbre des objets en plaçant les enfants dans une nouvelle propriété enfants à l'intérieur de l'objet parent.

Code:

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

Usage:

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

Production:

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
                    (
                    )
                  )
                )
              )
            )
          )
        )
      )
    )
  )
)
Nicolai
la source
Une structure arborescente dans WordPress n'est pas un tableau multidimensionnel. Il s'agit d'un tableau d'objets avec des informations sur la filiation.
Matt van Andel
J'ai essayé environ 10 solutions différentes pour ce problème. Merci pour cette excellente solution, elle le garde dans une belle structure d'objet WP. Cela doit être accepté en fait!
Drmzindec
@JohanPretorius Merci et mon plaisir. Eh bien, les gens recherchent des choses différentes. Je suppose que le PO a trouvé l'autre réponse plus utile. C'est parfait.
Nicolai
1

Version modifiée de la réponse acceptée où elle prend en considération la menu_orderpropriété des éléments de menu afin de conserver l'ordre correct du réseau plat d'origine. menu_orderest défini automatiquement par WordPress donc pas besoin de vérifier quoi que ce soit:

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

Usage:

// get navs
$locations = get_nav_menu_locations();

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

Il peut y avoir un malentendu ici à propos des éléments du menu de navigation WordPress en tant que structures arborescentes.

Les structures arborescentes dans WordPress NE SONT PAS DES RÉSEAUX MULTIDIMENSIONNELS!

Notez que bien que le tableau des éléments de menu renvoyés soit plat, il s'agit toujours d'une structure arborescente car chaque élément contient des informations sur sa filiation (la valeur parent est soit 0 si l'élément n'a pas de parent - soit l'id de l'élément parent s'il Est-ce que).

Lorsque vous passez un tel tableau à la Walkerclasse, il parcourt les résultats et crée deux tableaux - un contenant des éléments de niveau supérieur et un autre contenant des éléments enfants au format $parent_id => array()où le tableau contient des éléments de menu qui sont des enfants directs de cet élément.

Le marcheur parcourt ensuite le tableau des éléments de niveau supérieur, traite cet élément, puis vérifie le tableau des enfants pour voir s'il y a des enfants pour l'élément actuel, et traite chacun d'eux de la même manière, de manière récursive.

Si vous voulez savoir comment convertir une structure arborescente WordPress en un tableau multidimensionnel, c'est une question complètement différente (et non techniquement une question WordPress). Mais les informations renvoyées par wp_get_nav_menu_items() sont une structure arborescente ... et vous pouvez les utiliser Walkertelles quelles pour les gérer.

Si vous voulez voir le code exact que la classe Walker de WordPress exécute pour parcourir le tableau, jetez un œil à Walker-> walk () sur WordPress Trac à partir des lignes 213-258 . Vous pouvez utiliser ce code tel quel pour créer un tableau multidimensionnel, si c'est ce que vous recherchez.

Marcheurs

WordPress est conçu pour utiliser la Walkerclasse pour traiter ses structures arborescentes. Si vous effectuez simplement le rendu d'un menu, ou si vous avez vraiment besoin de la sortie ultime, vous pouvez envisager d'utiliser la wp_nav_menu()sortie de votre menu…

Exemple:

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

Vous étendre la classe Walker (par exemple Your_Walker()) pour obtenir la sortie dont vous avez besoin. Pour un exemple, voir cette entrée sur le Codex .

Matt van Andel
la source
2
Dans l'option A, $sorted_menu_itemsest toujours un tableau "plat" et la sortie de l'option B est une chaîne.
birgire
Je pense qu'il y a un malentendu sur la façon dont WordPress définit les "structures arborescentes". wp_get_nav_menu_items()renvoie une structure arborescente, c'est-à-dire un tableau dans lequel chaque élément contient des données de parenté. Ces structures sont destinées à être rendues avec une Walkerclasse. Si le cas d'utilisation ici implique simplement la conversion d'un tableau "plat" en un tableau multidimensionnel basé sur des données de parenté (par exemple 'post_parent' => 123), cette question ne concerne pas techniquement WordPress et doit être déplacée vers Stack Overflow.
Matt van Andel
1
Écoutez, je me fiche de ce que WordPress définit comme des "structures arborescentes" (je ne pense pas que ce sentiment ait même un sens).
YemSalat
Vous n'obtiendrez PAS cela comme comportement WordPress par défaut. Comme d'autres l'ont déclaré, vous disposez de toutes les informations dont vous avez besoin pour restructurer le tableau comme vous le souhaitez, et je vous ai lié à des zones spécifiques du noyau WordPress à utiliser comme référence. Ce n'est pas vraiment une question WordPress autant qu'une question PHP. Vous pouvez soit utiliser la classe Walker telle quelle, soit copier les lignes pertinentes de Walker :: walk () comme je l'ai indiqué, pour construire votre tableau.
Matt van Andel