Converter saída de itens nav_menu em uma matriz multidimensional do tipo árvore


11

Existe alguma maneira de pegar os itens do menu de navegação como uma matriz multidimensional em vez de uma matriz plana?

Por uma estrutura em forma de árvore, quero dizer algo que preservaria o relacionamento entre itens filho e pai, assim (este é apenas um exemplo)…

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

Existe uma wp_get_nav_menu_items()função, mas ela retorna uma matriz unidimensional com todos os itens no mesmo nível, o que não é o que eu quero. O WordPress inclui uma maneira integrada de obter uma matriz multidimensional para meus itens de menu? Caso contrário, qual é a melhor maneira de obter wp_get_nav_menu_items()como estrutura de árvore uma matriz multidimensional em termos de desempenho?


3
essa matriz unidimensional contém todos os dados necessários para construir uma árvore se você usar uma função recursiva. para cada um dos IDs do item de menu, procure outros itens de menu com o ID correspondente no campo pai do objeto, esses serão seus filhos.
Milo

Eu sei que posso fazer uma árvore com isso, mas eu queria saber se já existe alguma opção no wp.
YemSalat

Qual é o seu caso de uso? A Walkerclasse lida com a profundidade dos itens de menu de navegação classificados automaticamente, mesmo que a matriz seja plana.
31515 Matt van Andel

1
Sua edição está errada. Editei o título de volta (mudei algumas palavras) A saída de nav_items é uma matriz plana, não é uma árvore em nenhum sentido. Meu caso de uso é: eu quero os itens de navegação como uma árvore, para que eu possa fazê-lo sozinho, sem precisar usar as abstrações quebradas do WP.
YemSalat 1/08

Esclarei a questão um pouco, para deixar mais claro o que quero.
YemSalat 01/08

Respostas:


21

O problema de construir uma árvore a partir de uma matriz plana foi resolvido aqui com esta solução recursiva levemente modificada:

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

onde adicionamos o wpse_childrenatributo prefixado para evitar a colisão de nomes.

Agora só precisamos definir uma função auxiliar simples:

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

Agora fica super fácil transformar um menu de navegação em sua estrutura de árvore com:

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

Para JSON, podemos simplesmente usar:

$json = json_encode( $tree );

Para uma versão ligeiramente diferente, na qual escolhemos os atributos manualmente, confira a primeira revisão desta resposta aqui .

Atualização: Walker Class

Aqui está uma idéia bastante superficial de como poderíamos tentar nos conectar à parte recursiva do display_element()método da Walkerclasse abstrata .

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

onde WPSE_Nav_Menu_Treeé uma extensão da 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);
   }
}

Isso pode nos dar uma abordagem alternativa, se funcionar.


obrigado, é sempre interessante e divertido ver abordagens diferentes para a solução de problemas - o seu parece muito legal +1. @ialocin
birgire

1
O mesmo aqui, mas já sabíamos quem votou :) Explorar possibilidades é divertido! O resto geralmente é como o trabalho na linha de montagem, o que é ... vamos apenas dizer que não é divertido.
Nicolai

Obrigado, eu esperava que houvesse uma função WP "nativa" para isso. Vou esperar um pouco mais para ver se alguém posta outras soluções, caso contrário, essa será a resposta escolhida.
YemSalat 29/07

Eu atualizei a resposta com outro tipo de abordagem @YemSalat
birgire

Uau! Isso faz minha mente girar. Eu nunca lidei com a classe Walker antes (eu sei que ela existe). Eu esperava que houvesse uma maneira mais eficiente de fazer isso com algumas consultas SQL, mas eu realmente não quero entrar na estrutura de banco de dados do WP. Por enquanto, eu preferiria sua primeira abordagem, onde ela passaria de bicicleta wp_get_nav_menu_itemsrecursivamente.
YemSalat 15:15

3

Resumindo, a função abaixo cria a árvore de objetos colocando filhos em uma nova propriedade filhos dentro do objeto pai.

Código:

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

Resultado:

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

Uma estrutura semelhante a uma árvore no WordPress não é uma matriz multidimensional. É uma matriz de objetos com informações sobre pais.
Matt van Andel

Tentei cerca de 10 soluções diferentes para esse problema. Obrigado por esta ótima solução, ela mantém uma boa estrutura de Objetos WP. Isso precisa ser aceito na verdade!
Drmzindec

@JohanPretorius Obrigado e meu prazer. Bem, as pessoas estão procurando coisas diferentes. Presumo que o OP achou a outra resposta mais útil. É tudo de bom.
Nicolai

1

Versão modificada da resposta aceita, que leva em consideração a menu_orderpropriedade dos itens de menu para manter a ordem correta da matriz plana original. menu_orderé definido automaticamente pelo WordPress, portanto, não é necessário verificar nada:

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

Pode haver um mal-entendido aqui sobre os itens do menu de navegação do WordPress como estruturas semelhantes a árvores.

Estruturas de árvore no WordPress NÃO SÃO ARTIGOS MULTIDIMENSIONAIS!

Observe que, embora a matriz de itens de menu retornada seja plana, ela ainda é uma estrutura semelhante a uma árvore, pois cada item contém informações sobre sua ascendência (o valor pai é 0 se o item não tiver pai - ou o ID do item pai se faz).

Quando você passa essa matriz para a Walkerclasse, ela percorre os resultados e cria duas matrizes - uma contendo itens de nível superior e outra contendo itens filhos no formato em $parent_id => array()que a matriz contém itens de menu que são filhos diretos desse elemento.

O caminhante percorre a matriz de itens de nível superior, processa esse item e verifica a matriz filho para ver se há filhos para o elemento atual e processa cada um deles da mesma maneira, recursivamente.

Se você deseja saber como converter uma estrutura semelhante a uma árvore do WordPress em uma matriz multidimensional, essa é uma pergunta completamente diferente (e não tecnicamente uma pergunta do WordPress). Mas as informações retornadas por wp_get_nav_menu_items() são uma estrutura semelhante a uma árvore ... e você pode usar Walkercomo está para lidar com isso.

Se você quiser ver o código exato que a classe Walker do WordPress executa para percorrer a matriz, dê uma olhada em Walker-> walk () no WordPress Trac das linhas 213-258 . Você pode usar esse código como está para criar uma matriz multidimensional, se é isso que você procura.

Caminhantes

O WordPress foi projetado para usar a Walkerclasse para processar suas estruturas semelhantes a árvores. Se você está simplesmente renderizando um menu, ou realmente precisa apenas da saída definitiva, considere usar wp_nav_menu()a saída do menu…

Exemplo:

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

Você estenderia a classe Walker (por exemplo Your_Walker()) para obter a saída necessária. Por exemplo, veja esta entrada no Codex .


2
Na opção A, $sorted_menu_itemsainda é uma matriz "plana" e a saída da opção B é uma sequência.
birgire

Eu acho que há um mal-entendido sobre como o WordPress define "estruturas em forma de árvore". wp_get_nav_menu_items()retorna uma estrutura semelhante a uma árvore - ou seja, uma matriz em que cada item contém dados de ascendência. Essas estruturas devem ser renderizadas com uma Walkerclasse. Se o caso de uso aqui envolver apenas a conversão de uma matriz "plana" em uma matriz multidimensional com base em dados de pais (por exemplo 'post_parent' => 123), essa pergunta não é tecnicamente sobre WordPress e deve ser movida para Stack Overflow.
Matt van Andel

1
Olha, eu não ligo para o que o WordPress define "estruturas em forma de árvore" (acho que esse sentido nem faz sentido). Tudo o que me interessa é ter uma matriz multidimensional, com a qual eu possa fazer as coisas por conta própria.
YemSalat 1/08

Você NÃO conseguirá isso como comportamento padrão do WordPress. Como outros já declararam, você tem todas as informações necessárias para reestruturar a matriz da maneira que desejar, e eu o vinculei a áreas específicas no núcleo do WordPress para usar como referência. Esta não é realmente uma pergunta sobre WordPress, nem sobre PHP. Você pode usar a classe Walker como está, ou pode copiar as linhas relevantes de Walker :: walk () como afirmei, para criar sua matriz.
Matt van Andel
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.