将nav_menu项的输出转换为树形多维数组


11

有什么办法可以将导航菜单项作为多维数组而不是平面数组来获取?

所谓树状结构,是指可以保留子项与父项之间关系的东西(仅是一个例子)……

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

有一个wp_get_nav_menu_items()函数,但是它返回一维数组,所有项目都在同一级别,这不是我想要的。WordPress是否包含内置方法来获取菜单项的多维数组?如果不是,wp_get_nav_menu_items()从性能上来说,将树状结构放入多维数组的最佳方法是什么?


3
如果使用递归函数,则该一维数组包含构建树所需的所有数据。对于每个菜单项ID,请在对象父字段中查找具有匹配ID的其他菜单项,这些菜单项将成为其子项。
米洛(Milo)2014年

我知道我可以用它制作一棵树,但是我想知道wp中是否已经有这样的选项。
YemSalat 2014年

您的用例是什么?的Walker类自动处理排序导航菜单项的深度,即使在阵列是平坦的。
马特·范·安德尔

1
您的编辑是错误的。我将标题编辑回去(更改了几个单词)nav_items的输出是一个平面数组,无论如何它都不是一棵树。我的用例是-我希望将导航项作为树,因此我可以自己对它进行精简,而不必使用WP损坏的抽象。
YemSalat

我对这个问题做了一些澄清,以使我更清楚想要什么。
YemSalat

Answers:


21

的问题,从平面阵列构建树已经解决了这里与此,略作修改,递归解决方案:

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

我们在其中添加了带前缀的wpse_children属性,以避免名称冲突。

现在我们只需要定义一个简单的辅助函数:

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

现在,使用以下命令将导航菜单转换为树形结构变得非常容易:

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

对于JSON,我们可以简单地使用:

$json = json_encode( $tree );

对于稍有不同的版本(我们在其中选择了属性),请在此处查看此答案的第一个修订版。

更新:沃克班

这是一个相当粗略的想法,我们如何尝试display_element()使用抽象Walker类方法的递归部分。

$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_Tree的扩展在哪里Walker_Nav_Menu

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

如果可行,这可能为我们提供了另一种方法。


谢谢,看到解决问题的不同方法总是很有趣,很有趣-您的+1看上去很酷。@ialocin
birgire

1
同样在这里,但我们已经知道谁投票了:)探索可能性很有趣!其余的通常就像流水线工作一样,这只是...说的不好玩。
Nicolai 2015年

谢谢,我希望有一个“本地”的WP函数。我将再等一会,看看是否有人发布了其他解决方案,否则将是选择的答案。
YemSalat

我用另一种方法@YemSalat更新了答案
birgire

哇!那使我心神混乱。我以前从未处理过Walker类(尽管我知道它已经存在)我希望可以通过几个SQL查询来实现一种更高效的方式,但是我真的不想进入WP db结构。现在,我希望您的第一种方法能够wp_get_nav_menu_items递归循环。
YemSalat 2015年

3

简而言之,贝娄函数确实通过将子代置于父对象内的新子代属性中来创建对象树。

码:

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

用法:

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

输出:

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

WordPress中的树状结构不是多维数组。它是带有育儿信息的对象数组。
Matt van Andel

针对此问题尝试了约10种不同的解决方案。感谢您提供的出色解决方案,它将其保持在一个不错的WP Object结构中。这实际上需要接受!
Drmzindec

@JohanPretorius谢谢,我很高兴。人们正在寻找不同的东西。我认为OP认为其他答案更有帮助。都很好。
Nicolai

1

接受答案的修改版本,其中考虑了menu_order菜单项的属性,以便保留原始平面阵列的正确顺序。menu_order由WordPress自动设置,因此无需检查任何内容:

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

用法:

// 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

关于WordPress导航菜单项为树状结构,这里可能存在误解。

WordPress中的树状结构不是多维数组!

请注意,虽然返回的菜单项数组是平坦的,但它仍然是树状结构,因为每个项都包含有关其父项的信息(如果该项没有父项,则父项值为0;如果该项没有父项,则父项的id确实)。

将此类数组传递给Walker类时,它将循环遍历结果并创建两个数组-一个包含顶级项,另一个包含子项,其格式$parent_id => array()为该数组包含作为该元素的直接子项的菜单项。

然后,walker遍历顶级项数组,处理该项,然后检查子级数组以查看当前元素是否有任何子级,并以相同的方式递归处理每个子级。

如果您想知道如何将WordPress树状结构转换为多维数组,则完全是另一个问题(从技术上讲不是WordPress问题)。但是返回的信息wp_get_nav_menu_items() 一个树状结构...您可以Walker按原样使用它。

如果您想查看WordPress的Walker类运行以遍历数组的确切代码,请从213-258行看一下WordPress Trac上的Walker-> walk()。您可以按原样使用该代码来构建多维数组。

步行者

WordPress旨在使用Walker该类来处理其树状结构。如果您只是在渲染菜单,或者真的只需要最终输出,则可以考虑使用wp_nav_menu()输出菜单...

例:

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

您将扩展Walker类(例如Your_Walker())以获取所需的任何输出。有关示例,请参见食品法典上的此项


2
在选项A中,$sorted_menu_items仍然是“平面”数组,选项B的输出是字符串。
birgire

我认为对WordPress如何定义“树状结构”存在误解。wp_get_nav_menu_items()返回一个类似树的结构-即一个数组,其中每个项目都包含父母身分数据。这些结构是用Walker类渲染的。如果此处的用例仅涉及基于亲子关系数据(例如'post_parent' => 123)将“平面”数组转换为多维数组,则此问题在技术上与WordPress无关,应移至Stack Overflow。
Matt van Andel

1
看,我不在乎WordPress定义了“树状结构”(我什至认为这种感觉都不合理),我所关心的只是拥有一个多维数组,我可以自己处理。
YemSalat

您不会将其作为默认的WordPress行为。正如其他人所说,您拥有按需要重组数组所需的所有信息,我将您链接到WordPress核心中的特定区域以用作参考。与PHP相比,这实际上不是WordPress问题。您可以按原样使用Walker类,也可以按照我所说的从Walker :: walk()复制相关行来构建数组。
马特·范·安德尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.