Преобразование вывода элементов nav_menu в древовидный многомерный массив

Есть ли способ захватить пункты меню навигации в виде многомерного массива вместо плоского массива?

По древовидной структуре я имею в виду то, что сохранит отношения между дочерними и родительскими элементами, например (это просто пример) …

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() но она возвращает 1-мерный массив со всеми элементами на одном уровне, чего я не хочу. Включает ли WordPress встроенный способ получения многомерного массива для моих пунктов меню? Если нет, то каков наилучший способ получить wp_get_nav_menu_items() как древовидную структуру в многомерный массив с точки зрения производительности?

Solutions Collecting From Web of "Преобразование вывода элементов nav_menu в древовидный многомерный массив"

Здесь была решена проблема построения дерева из плоского массива с помощью этого слегка измененного рекурсивного решения:

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

Для немного другой версии, где мы отобрали атрибуты, ознакомьтесь с первой версией этого ответа здесь .

Обновление: Класс Walker

Вот довольно отрывочная идея о том, как мы могли бы попытаться подключиться к рекурсивной части метода 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); } } 

Это может дать нам альтернативный подход, если он будет работать.

Короче говоря, функция bellow создает дерево объектов, помещая детей в новое дочернее свойство внутри родительского объекта.

Код:

 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 как древовидных структурах.

Деревоподобные структуры в WordPress не являются многомерными массивами!

Обратите внимание, что, хотя массив возвращаемых элементов меню является плоским, он по-прежнему является древовидной структурой, потому что каждый элемент содержит информацию о его происхождении (родительское значение равно либо 0, если элемент не имеет родителя, или идентификатор родительского элемента, если он делает).

Когда вы передаете такой массив классу Walker , он просматривает результаты и создает два массива: один содержит элементы верхнего уровня, а другой содержит дочерние элементы в формате $parent_id => array() где массив содержит пункты меню, которые прямых детей этого элемента.

Затем ходок перемещается через массив элементов верхнего уровня, обрабатывает этот элемент, а затем проверяет массив children, чтобы увидеть, есть ли какие-либо дочерние элементы для текущего элемента, и обрабатывает каждый из них одинаково, рекурсивно.

Если вы хотите знать, как преобразовать древовидную структуру WordPress в многомерный массив, это совсем другой вопрос (а не технический вопрос WordPress). Но информация, возвращаемая wp_get_nav_menu_items() является древовидной структурой … и вы можете использовать Walker as-is для ее обработки.

Если вы хотите увидеть точный код, который выполняет класс Walker WordPress для перемещения по массиву, посмотрите на Walker-> walk () на WordPress Trac из строк 213-258 . Вы можете использовать этот код как есть для построения многомерного массива, если это то, что вам нужно.

Ходоки

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() ), чтобы получить Your_Walker() вам результат. Например, см. Эту запись в Codex .