wp_get_nav_menu_items()
云策文档标注
概述
wp_get_nav_menu_items() 函数用于检索指定导航菜单的所有菜单项,返回一个包含菜单项对象的数组。它基于 get_posts() 查询菜单项文章,并支持多种参数来控制输出顺序和内容。
关键要点
- 函数接受菜单 ID、slug、名称或 WP_Term 对象作为第一个参数,第二个参数为可选的数组参数,用于传递给 get_posts()。
- 默认参数包括 order、orderby、post_type、post_status 等,其中 output_key 参数专门用于影响最终菜单项的输出顺序。
- 函数内部使用 wp_setup_nav_menu_item() 处理菜单项,并在前端通过 _is_valid_nav_menu_item 过滤无效项。
- 支持 wp_get_nav_menu_items 过滤器钩子,允许开发者自定义返回的菜单项数组。
- 返回数组或 false,若菜单不存在或 taxonomy 'nav_menu' 不存在则返回 false。
代码示例
// 基本用法:获取菜单项
$menu_items = wp_get_nav_menu_items( 'primary-menu' );
// 使用参数控制输出
$args = array(
'order' => 'DESC',
'orderby' => 'title',
'output' => ARRAY_A,
'output_key' => 'menu_order'
);
$menu_items = wp_get_nav_menu_items( $menu_id, $args );注意事项
- 大多数 $args 参数(除 output_key 外)是传递给 get_posts() 的,可能仅间接影响最终菜单项的排序和内容。
- 在 WordPress 6.0 及以上版本,确保 pre_get_posts 操作未意外修改查询(如设置 post_type),否则可能导致返回空数组。
- 函数在非管理界面会自动过滤无效菜单项,但在管理界面不会执行此过滤。
原文内容
Retrieves all menu items of a navigation menu.
Description
Note: Most arguments passed to the $args parameter – save for ‘output_key’ – are specifically for retrieving nav_menu_item posts from get_posts() and may only indirectly affect the ultimate ordering and content of the resulting nav menu items that get returned from this function.
Parameters
$menuint|string|WP_Termrequired-
Menu ID, slug, name, or object.
$argsarrayoptional-
Arguments to pass to get_posts() .
orderstringHow to order nav menu items as queried with get_posts() .
Will be ignored if'output'is ARRAY_A. Default'ASC'.orderbystringField to order menu items by as retrieved from get_posts() .
Supply an orderby field via'output_key'to affect the output order of nav menu items. Default'menu_order'.post_typestringMenu items post type. Default'nav_menu_item'.post_statusstringMenu items post status. Default'publish'.outputstringHow to order outputted menu items. Default ARRAY_A.output_keystringKey to use for ordering the actual menu items that get returned. Note that that is not a get_posts() argument and will only affect output of menu items processed in this function. Default'menu_order'.nopagingboolWhether to retrieve all menu items (true) or paginate (false). Default true.update_menu_item_cacheboolWhether to update the menu item cache. Default true.
More Arguments from get_posts( … $args )
Arguments to retrieve posts. See WP_Query::parse_query() for all available arguments.
numberpostsintTotal number of posts to retrieve. Is an alias of$posts_per_pagein WP_Query. Accepts -1 for all. Default 5.categoryint|stringCategory ID or comma-separated list of IDs (this or any children).
Is an alias of$catin WP_Query. Default 0.includeint[]An array of post IDs to retrieve, sticky posts will be included.
Is an alias of$post__inin WP_Query.excludeint[]An array of post IDs not to retrieve.suppress_filtersboolWhether to suppress filters. Default true.
Default:
array()
Source
function wp_get_nav_menu_items( $menu, $args = array() ) {
$menu = wp_get_nav_menu_object( $menu );
if ( ! $menu ) {
return false;
}
if ( ! taxonomy_exists( 'nav_menu' ) ) {
return false;
}
$defaults = array(
'order' => 'ASC',
'orderby' => 'menu_order',
'post_type' => 'nav_menu_item',
'post_status' => 'publish',
'output' => ARRAY_A,
'output_key' => 'menu_order',
'nopaging' => true,
'update_menu_item_cache' => true,
'tax_query' => array(
array(
'taxonomy' => 'nav_menu',
'field' => 'term_taxonomy_id',
'terms' => $menu->term_taxonomy_id,
),
),
);
$args = wp_parse_args( $args, $defaults );
if ( $menu->count > 0 ) {
$items = get_posts( $args );
} else {
$items = array();
}
$items = array_map( 'wp_setup_nav_menu_item', $items );
if ( ! is_admin() ) { // Remove invalid items only on front end.
$items = array_filter( $items, '_is_valid_nav_menu_item' );
}
if ( ARRAY_A === $args['output'] ) {
$items = wp_list_sort(
$items,
array(
$args['output_key'] => 'ASC',
)
);
$i = 1;
foreach ( $items as $k => $item ) {
$items[ $k ]->{$args['output_key']} = $i++;
}
}
/**
* Filters the navigation menu items being returned.
*
* @since 3.0.0
*
* @param array $items An array of menu item post objects.
* @param object $menu The menu object.
* @param array $args An array of arguments used to retrieve menu item objects.
*/
return apply_filters( 'wp_get_nav_menu_items', $items, $menu, $args );
}
Hooks
- apply_filters( ‘wp_get_nav_menu_items’, array $items, object $menu, array $args )
-
Filters the navigation menu items being returned.
Changelog
| Version | Description |
|---|---|
| 3.0.0 | Introduced. |
Skip to note 12 content
Stefano
Building bootstrap 3 menu with submenu items without use WP_nav_walker (boostrap)! (Require bootstrap.css and bootstrap.js)
' ."n"; $menu_list .= '<div class="container-fluid">' ."n"; $menu_list .= '<!-- Brand and toggle get grouped for better mobile display -->' ."n"; $menu_list .= '<div class="navbar-header">' ."n"; $menu_list .= '<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">' ."n"; $menu_list .= '<span class="sr-only">Toggle navigation</span>' ."n"; $menu_list .= '<span class="icon-bar"></span>' ."n"; $menu_list .= '<span class="icon-bar"></span>' ."n"; $menu_list .= '<span class="icon-bar"></span>' ."n"; $menu_list .= '</button>' ."n"; $menu_list .= '<a class="navbar-brand" href="' . home_url() . '">' . get_bloginfo( 'name' ) . '</a>'; $menu_list .= '</div>' ."n"; $menu_list .= '<!-- Collect the nav links, forms, and other content for toggling -->'; $menu = get_term( $locations[$theme_location], 'nav_menu' ); $menu_items = wp_get_nav_menu_items($menu->term_id); $menu_list .= '<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">' ."n"; $menu_list .= '<ul class="nav navbar-nav">' ."n"; foreach( $menu_items as $menu_item ) { if( $menu_item->menu_item_parent == 0 ) { $parent = $menu_item->ID; $menu_array = array(); foreach( $menu_items as $submenu ) { if( $submenu->menu_item_parent == $parent ) { $bool = true; $menu_array[] = '<li><a href="' . $submenu->url . '">' . $submenu->title . '</a></li>' ."n"; } } if( $bool == true && count( $menu_array ) > 0 ) { $menu_list .= '<li class="dropdown">' ."n"; $menu_list .= '<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">' . $menu_item->title . ' <span class="caret"></span></a>' ."n"; $menu_list .= '<ul class="dropdown-menu">' ."n"; $menu_list .= implode( "n", $menu_array ); $menu_list .= '</ul>' ."n"; } else { $menu_list .= '<li>' ."n"; $menu_list .= '<a href="' . $menu_item->url . '">' . $menu_item->title . '</a>' ."n"; } } // end <li> $menu_list .= '</li>' ."n"; } $menu_list .= '</ul>' ."n"; $menu_list .= '</div>' ."n"; $menu_list .= '</div><!-- /.container-fluid -->' ."n"; $menu_list .= '</nav>' ."n"; } else { $menu_list = '<!-- no menu defined in location "'.$theme_location.'" -->'; } echo $menu_list; } ?>if( $menu_item->menu_item_parent == 0 )Otherwise there will be several closing li tags not closing anything for all submenu items since they have parents and won’t pass this check.Skip to note 13 content
Andrija Naglic
Building menu list with children (submenus) and selecting the menu by it’s location:
// Intented to use with locations, like 'primary' // clean_custom_menu("primary"); #add in your theme functions.php file function clean_custom_menu( $theme_location ) { if ( ($theme_location) && ($locations = get_nav_menu_locations()) && isset($locations[$theme_location]) ) { $menu = get_term( $locations[$theme_location], 'nav_menu' ); $menu_items = wp_get_nav_menu_items($menu->term_id); $menu_list = '<nav>' ."n"; $menu_list .= '<ul class="main-nav">' ."n"; $count = 0; $submenu = false; foreach( $menu_items as $menu_item ) { $link = $menu_item->url; $title = $menu_item->title; if ( !$menu_item->menu_item_parent ) { $parent_id = $menu_item->ID; $menu_list .= '<li class="item">' ."n"; $menu_list .= '<a href="'.$link.'" class="title">'.$title.'</a>' ."n"; } if ( $parent_id == $menu_item->menu_item_parent ) { if ( !$submenu ) { $submenu = true; $menu_list .= '<ul class="sub-menu">' ."n"; } $menu_list .= '<li class="item">' ."n"; $menu_list .= '<a href="'.$link.'" class="title">'.$title.'</a>' ."n"; $menu_list .= '</li>' ."n"; if ( $menu_items[ $count + 1 ]->menu_item_parent != $parent_id && $submenu ){ $menu_list .= '</ul>' ."n"; $submenu = false; } } if ( $menu_items[ $count + 1 ]->menu_item_parent != $parent_id ) { $menu_list .= '</li>' ."n"; $submenu = false; } $count++; } $menu_list .= '</ul>' ."n"; $menu_list .= '</nav>' ."n"; } else { $menu_list = '<!-- no menu defined in location "'.$theme_location.'" -->'; } echo $menu_list; }Skip to note 14 content
Cesar Jefferson Aquino Maximiliano
Get simple array of menu.
function wp_get_menu_array($current_menu) { $array_menu = wp_get_nav_menu_items($current_menu); $menu = array(); foreach ($array_menu as $m) { if (empty($m->menu_item_parent)) { $menu[$m->ID] = array(); $menu[$m->ID]['ID'] = $m->ID; $menu[$m->ID]['title'] = $m->title; $menu[$m->ID]['url'] = $m->url; $menu[$m->ID]['children'] = array(); } } $submenu = array(); foreach ($array_menu as $m) { if ($m->menu_item_parent) { $submenu[$m->ID] = array(); $submenu[$m->ID]['ID'] = $m->ID; $submenu[$m->ID]['title'] = $m->title; $submenu[$m->ID]['url'] = $m->url; $menu[$m->menu_item_parent]['children'][$m->ID] = $submenu[$m->ID]; } } return $menu; }function get_menu($current_menu) { $array_menu = wp_get_nav_menu_items($current_menu); $menu = array(); $refs = array(); foreach ($array_menu as $m) { if (empty($m->menu_item_parent)) { $curMenu = array(); $curMenu['id'] = $m->ID; $curMenu['title'] = $m->title; $curMenu['url'] = $m->url; $curMenu['children'] = array(); $refs[$m->ID] = count($menu); array_push($menu, $curMenu); } else { $submenu = array(); $submenu['id'] = $m->ID; $submenu['title'] = $m->title; $submenu['url'] = $m->url; array_push($menu[$refs[$m->menu_item_parent]]['children'], $submenu); } } return $menu; }function wp_get_nested_menu_array($current_menu) { $array_menu = wp_get_nav_menu_items($current_menu); $menu = array(); foreach ($array_menu as $m) { if (empty($m->menu_item_parent)) { $menu[$m->ID] = array(); $menu[$m->ID]['ID'] = $m->ID; $menu[$m->ID]['title'] = $m->title; $menu[$m->ID]['url'] = $m->url; $menu[$m->ID]['children'] = array(); } } $submenu = array(); foreach ($array_menu as $m) { if ($m->menu_item_parent) { $submenu[$m->ID] = array(); $submenu[$m->ID]['ID'] = $m->ID; $submenu[$m->ID]['title'] = $m->title; $submenu[$m->ID]['url'] = $m->url; $submenu[$m->ID]['parent'] = $m->menu_item_parent; if (isset($submenu[$m->menu_item_parent])) { $submenu[$m->menu_item_parent]['children'][$m->ID] = $submenu[$m->ID]; $mainparentid = $submenu[$m->menu_item_parent]['parent']; $menu[$mainparentid]['children'][$m->menu_item_parent] = $submenu[$m->menu_item_parent]; }else{ $menu[$m->menu_item_parent]['children'][$m->ID] = $submenu[$m->ID]; } } } return $menu; }Skip to note 15 content
Codex
Building simple menu list
// Get the nav menu based on $menu_name (same as 'theme_location' or 'menu' arg to wp_nav_menu) // This code based on wp_nav_menu's code to get Menu ID from menu slug $menu_name = 'custom_menu_slug'; if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) { $menu = wp_get_nav_menu_object( $locations[ $menu_name ] ); $menu_items = wp_get_nav_menu_items($menu->term_id); $menu_list = '<ul id="menu-' . $menu_name . '">'; foreach ( (array) $menu_items as $key => $menu_item ) { $title = $menu_item->title; $url = $menu_item->url; $menu_list .= '<li><a href="' . $url . '">' . $title . '</a></li>'; } $menu_list .= '</ul>'; } else { $menu_list = '<ul><li>Menu "' . $menu_name . '" not defined.</li></ul>'; } // $menu_list now ready to outputSkip to note 16 content
Aslam Ansari
/** * This code will generate a simple array of menu items in hierarchical(parent-child) order. * This array will also contain child field as "true" if the item has child items array, * and custom active class if the link is active. */ $menu_lists = array(); $sub_parent = 0; // Menu item array (note: "Menu 1" is the name of menu) $menu_items = wp_get_nav_menu_items( 'Menu 1' ); foreach ( $menu_items as $menu_item ) { if ( in_array( $menu_item->object, array( 'page', 'custom' ) ) ) { $id = $menu_item->ID; $title = $menu_item->title; $link = $menu_item->url; $menu_item_parent = $menu_item->menu_item_parent; // if menu item has no parent, means this is the top-menu. if ( ! $menu_item_parent ) { $menu_lists[ $id ]['child'] = false; $menu_lists[ $id ]['id'] = $id; $menu_lists[ $id ]['title'] = $title; $menu_lists[ $id ]['link'] = $link; // add active field if current link and open url is same. if ( get_permalink() === $link ) { $menu_lists[ $id ]['active'] = 'current-menu-item'; } } else { // if parent menu is set, means this is 2nd level menu if ( isset( $menu_lists[ $menu_item_parent ] ) ) { $menu_lists[ $menu_item_parent ]['child'] = true; $menu_lists[ $menu_item_parent ][ $id ]['child'] = false; $menu_lists[ $menu_item_parent ][ $id ]['id'] = $id; $menu_lists[ $menu_item_parent ][ $id ]['title'] = $title; $menu_lists[ $menu_item_parent ][ $id ]['link'] = $link; // add active field to current menu item and its parent menu item if current link and open url is same. if ( get_permalink() === $link ) { $menu_lists[ $menu_item_parent ]['active'] = 'current-menu-item'; $menu_lists[ $menu_item_parent ][ $id ]['active'] = 'current-menu-item'; } $sub_parent = $menu_item_parent; } elseif ( isset( $menu_lists[ $sub_parent ][ $menu_item_parent ] ) ) { // if parent menu is set and their parent menu is also set, means this is 3rd level menu $menu_lists[ $sub_parent ][ $menu_item_parent ]['child'] = true; $menu_lists[ $sub_parent ][ $menu_item_parent ][ $id ]['id'] = $id; $menu_lists[ $sub_parent ][ $menu_item_parent ][ $id ]['title'] = $title; $menu_lists[ $sub_parent ][ $menu_item_parent ][ $id ]['link'] = $link; // add active field to current menu item and its parent menu item if current link and open url is same. if ( get_permalink() === $link ) { $menu_lists[ $sub_parent ]['active'] = 'current-menu-item'; $menu_lists[ $sub_parent ][ $menu_item_parent ]['active'] = 'current-menu-item'; $menu_lists[ $sub_parent ][ $menu_item_parent ][ $id ]['active'] = 'current-menu-item'; } } } } }Skip to note 17 content
rachelboylson
Create a shortcode that takes a menu ID and prints a simple list of links. Add this to your functions.php file.
Shortcode syntax: [my_custom_navigation menu_id=123]
// load navigation shortcode add_shortcode( 'wpdocs_custom_navigation', 'wpdocs_add_custom_navigation' ); // create shortcode for navigation function wpdocs_add_custom_navigation( $attributes, $content = null ) { // shortcode takes in one argument which is the menu id $navigation_attributes = shortcode_atts( array( 'menu_id' => '', ), $attributes ); // get navigation menu items based on menu id passed from shortcode $menu_items = wp_get_nav_menu_items( $navigation_attributes['menu_id'] ); // create an empty string which we will add onto $menu_html = ''; // loop through array and build list of navigation links for ( $x = 0; $x < count( $menu_items ); $x++ ) { // get URL of menu item $url = $menu_items[ $x ]->url; // get title of menu item $title = $menu_items[ $x ]->title; // build HTML $menu_html .= '<a href="'; $menu_html .= esc_attr( esc_url( $url ) ); $menu_html .= '">'; $menu_html .= wp_kses_post( $title ); $menu_html .= '</a><br>'; } // return navigation HTML return '<div id="menuWrapper">' . $menu_html . '</div>'; }Skip to note 18 content
Saad Amin
I have tested this menu , it supports sub menu , submenu will have arrow , also current page parent and menu item will have active class.
$menu_name = 'header-menu'; if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) { $menu = wp_get_nav_menu_object( $locations[ $menu_name ] ); $menu_items = wp_get_nav_menu_items($menu->term_id); $menu_list = ''; $count = 0; $submenu = false;$cpi=get_the_id(); foreach( $menu_items as $current ) { if($cpi == $current->object_id ){if ( !$current->menu_item_parent ) {$cpi=$current->ID;}else{$cpi=$current->menu_item_parent;}$cai=$current->ID;break;} } foreach( $menu_items as $menu_item ) { $link = $menu_item->url; $title = $menu_item->title; $menu_item->ID==$cai ? $ac2=' current_menu' : $ac2=''; if ( !$menu_item->menu_item_parent ) { $parent_id = $menu_item->ID;$parent_id==$cpi ? $ac=' current_item' : $ac=''; if(!empty($menu_items[$count + 1]) && $menu_items[ $count + 1 ]->menu_item_parent == $parent_id ){//Checking has child $menu_list .= '<li class="dropdown has_child'.$ac.'"><a href="'.$link.'" class="dropdown-toggle'.$ac2.'" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="nav-span"></span>'.$title.'<span class="caret"></span></a>'; }else{ $menu_list .= '<li class="'.$ac.'">' ."n";$menu_list .= '<a href="'.$link.'" class="'.$ac2.'">'.$title.'</a>' ."n"; } } if ( $parent_id == $menu_item->menu_item_parent ) { if ( !$submenu ) { $submenu = true; $menu_list .= '<ul class="dropdown-menu">' ."n"; } $menu_list .= '<li class="item">' ."n"; $menu_list .= '<a href="'.$link.'" class="'.$ac2.'">'.$title.'</a>' ."n"; $menu_list .= '</li>' ."n"; if(empty($menu_items[$count + 1]) || $menu_items[ $count + 1 ]->menu_item_parent != $parent_id && $submenu){ $menu_list .= '</ul>' ."n"; $submenu = false; } } if (empty($menu_items[$count + 1]) || $menu_items[ $count + 1 ]->menu_item_parent != $parent_id ) { $menu_list .= '</li>' ."n"; $submenu = false; } $count++; } } else { $menu_list = '<li>Menu "' . $menu_name . '" not defined.</li>'; } }Skip to note 19 content
mathiasmadsen
Here are two functions that can be used to add the menu to the REST API and retrieve individual menu items:
function wpdocs_menu_route() { $menuLocations = get_nav_menu_locations(); // Get nav locations set in theme, usually functions.php) return $menuLocations; } add_action( 'rest_api_init', function () { register_rest_route( 'custom', '/menu/', array( 'methods' => 'GET', 'callback' => 'wpdocs_menu_route', ) ); } ); function wpdocs_menu_single( $data ) { $menuID = $data['id']; // Get the menu from the ID $primaryNav = wp_get_nav_menu_items( $menuID ); // Get the array of wp objects, the nav items for our queried location. return $primaryNav; } add_action( 'rest_api_init', function () { register_rest_route( 'custom', '/menu/(?P<id>d+)', array( 'methods' => 'GET', 'callback' => 'wpdocs_menu_single', ) ); } );This allows us to access both:
https://example.com/wp-json/custom/menuandhttps://example.com/wp-json/custom/menu/{menuID}Skip to note 20 content
Hay
If you’re having issues with this function returning empty arrays since version 6.0 (as i did) make sure you’re not accidentally adjusting the
$query, for example by setting thepost_typeto a custom type in apre_get_postsaction.Skip to note 21 content
rohit0106
Build 3 level hierarchical menu
function create_bootstrap_menu( $theme_location ) { if ( ($theme_location) && ($locations = get_nav_menu_locations()) && isset($locations[$theme_location]) ) { $menu = get_term( $locations[$theme_location], 'nav_menu' ); $menu_items = wp_get_nav_menu_items($menu->term_id); //echo '<pre>'; print_r($menu_items); die; $menu_list = '<ul>'; $menucount = 1; $bool = true; foreach( $menu_items as $menu_item ) { if( $menu_item->menu_item_parent == 0 ) { $parent = $menu_item->ID; $menu_array = array(); foreach( $menu_items as $submenu ) { if( $submenu->menu_item_parent == $parent ) { $bool = true; $menu_array[] = '<li class="dropdown"><a href="' . $submenu->url . '" >' . $submenu->title . '</a><ul>'; $parents = $submenu->ID; foreach($menu_items as $submenus){ if( $submenus->menu_item_parent == $parents ) { $menu_array[] .= '<li><a href="' . $submenus->url . '" >' . $submenus->title . '</a></li>'; } } $menu_array[] .= '</ul></li>'; } } if( $bool == true && count( $menu_array ) > 0 ) { $menu_list .= '<li class="dropdown">'; $menu_list .= '<a href="'.$menu_item->url.'"><span>'.$menu_item->title.'</span> <i class="bi bi-chevron-down"></i></a>'; $menu_list .= '<ul>' ."n"; $menu_list .= implode( $menu_array ); $menu_list .= '</ul>'; } else { // echo "<pre>"; print_r($menu_item); $menu_list .= '<li>'; $menu_list .= '<a class="nav-link scrollto active" href="'.$menu_item->url.'">' . $menu_item->title . '</a>'; } } // end <li> $menu_list .= '</li>'; $menucount++; } } else { $menu_list = '<!-- no menu defined in location "'.$theme_location.'" -->'; } return $menu_list; }Skip to note 22 content
ricjoh
Here’s a simple recursive solution to get an array of a menu and all submenus:
function wpdocs_recursive_menu_array( $current_menu ) { $menudata = wp_get_nav_menu_items( $current_menu ); function _build( $ID, $data ) { $menu = array(); foreach ( $data as $datum ) { if ( $datum->menu_item_parent === $ID ) { $menu[ $datum->ID ] = array( 'ID' => $datum->ID, 'title' => $datum->title, 'url' => $datum->url, 'children' => _build( $datum->ID, $data ) ); } } return $menu; } return _build( 0, $menudata ); }