各位观众老爷们,大家好!我是你们的导游,今天咱们要一起深入 WordPress 的腹地,扒一扒 wp_get_nav_menu_items()
这个函数的底裤,看看它到底是怎么把菜单项从数据库里捞出来,再像搭积木一样,构建出我们看到的层级结构的。
准备好了吗?系好安全带,发车!
第一站:wp_get_nav_menu_items()
的入口
首先,我们得找到入口。wp_get_nav_menu_items()
函数的定义在 wp-includes/nav-menu.php
文件里。 它的核心作用就是:根据给定的菜单 ID 或菜单对象,从数据库中获取菜单项,并返回一个包含所有菜单项的数组。
函数签名如下:
/**
* Retrieve all menu items.
*
* @since 3.0.0
*
* @param mixed $menu Menu ID, slug, name, or object.
* @param array $args {
* Optional. Array of get_posts() arguments.
*
* @type string $order How to order navigation menu items. Accepts 'ASC', 'DESC'.
* Default 'ASC'.
* @type string $orderby Field to order navigation menu items by. Accepts 'id',
* 'menu_order', 'title'. Default 'menu_order'.
* @type bool $output Type of output. Accepts ARRAY_A, ARRAY_N, OBJECT, OBJECT_K.
* Default OBJECT.
* @type string $output_key The key to use for the 'OBJECT_K' output format.
* Default 'ID'.
* @type bool $update_post_term_cache Whether to update the post term cache.
* Default true.
* }
* @return WP_Post[]|false Array of menu items, false if menu not found.
*/
function wp_get_nav_menu_items( $menu, $args = array() ) {
// ... 函数主体
}
简单来说,你需要给它提供一个菜单的标识(可以是 ID,slug,name 或者对象),它就会返回一个包含菜单项的 WP_Post
对象数组。
第二站:参数处理和安全检查
进入函数内部,首先会进行一些参数处理和安全检查,确保传入的 $menu
是有效的,并且参数 $args
也符合预期。
$menu_object = wp_get_nav_menu_object( $menu );
if ( ! $menu_object ) {
return false;
}
$menu_id = is_object( $menu_object ) ? $menu_object->term_id : (int) $menu_object;
$defaults = array(
'order' => 'ASC',
'orderby' => 'menu_order',
'output' => OBJECT,
'output_key' => 'ID',
'update_post_term_cache' => true,
);
$args = wp_parse_args( $args, $defaults );
这里先用 wp_get_nav_menu_object()
函数来获取菜单对象,如果获取失败,直接返回 false
。 接着提取菜单的 ID。然后,使用 wp_parse_args()
函数将传入的 $args
参数与默认参数合并,保证所有需要的参数都有值。
第三站:构建查询语句
接下来,重头戏来了!我们要构建查询语句,从数据库中捞取菜单项。 wp_get_nav_menu_items()
函数的核心就在于构建正确的 WP_Query
参数,以获取所需的菜单项。
$args['post_type'] = 'nav_menu_item';
$args['posts_per_page'] = -1; // Retrieve all menu items.
$args['tax_query'] = array(
array(
'taxonomy' => 'nav_menu',
'field' => 'term_id',
'terms' => $menu_id,
),
);
$q = new WP_Query( $args );
$posts = $q->get_posts();
这里做了几件事:
$args['post_type'] = 'nav_menu_item';
: 指定要查询的 post 类型为nav_menu_item
,这是一种特殊的 post 类型,用于存储菜单项。$args['posts_per_page'] = -1;
: 设置每页显示的 post 数量为 -1,表示获取所有菜单项。$args['tax_query']
: 设置分类查询条件。nav_menu
是一个分类,term_id
是分类的 ID,$menu_id
就是我们之前获取的菜单 ID。通过这个分类查询,我们可以获取指定菜单下的所有菜单项。$q = new WP_Query( $args );
: 使用WP_Query
类创建一个新的查询对象,传入$args
参数。$posts = $q->get_posts();
: 执行查询,获取查询结果,返回一个包含WP_Post
对象的数组。
第四站:处理查询结果
拿到菜单项的数组后,还需要进行一些处理,比如更新 post 的 term cache。
if ( $args['update_post_term_cache'] ) {
wp_update_post_term_cache( $posts );
}
return $posts;
wp_update_post_term_cache()
函数用于更新 post 的 term cache,提高后续访问 post 的 term 的效率。
到这里,wp_get_nav_menu_items()
函数就完成了它的使命,把菜单项从数据库里捞了出来,并返回了一个包含 WP_Post
对象的数组。
第五站:构建层级结构 (重点!)
虽然 wp_get_nav_menu_items()
函数返回了菜单项,但这些菜单项是扁平的,没有层级关系。 要构建层级结构,我们需要自己动手。 WordPress 提供了 wp_get_nav_menu_items()
函数返回的菜单项数组,然后开发者可以根据 menu_item_parent
字段来构建层级关系。 下面是一个示例函数,用于构建菜单项的层级结构:
/**
* Build a hierarchical array of menu items.
*
* @param array $menu_items An array of menu items.
* @return array A hierarchical array of menu items.
*/
function build_menu_tree( $menu_items ) {
$parents = array();
$children = array();
// Separate menu items into parents and children.
foreach ( $menu_items as $item ) {
if ( 0 == $item->menu_item_parent ) {
$parents[$item->ID] = $item;
} else {
$children[$item->menu_item_parent][] = $item;
}
}
// Build the tree.
foreach ( $parents as $parent_id => $parent ) {
if ( isset( $children[$parent_id] ) ) {
$parent->children = $children[$parent_id];
} else {
$parent->children = array();
}
}
return $parents;
}
这个函数的核心思想是:
- 分离父节点和子节点: 遍历菜单项数组,将
menu_item_parent
为 0 的菜单项放入$parents
数组,表示它们是顶级菜单项。 将menu_item_parent
不为 0 的菜单项放入$children
数组,表示它们是子菜单项。 - 构建树形结构: 遍历
$parents
数组,对于每个父节点,检查$children
数组中是否存在以该父节点 ID 为键的子节点数组。 如果存在,则将该子节点数组赋值给父节点的children
属性。 如果不存在,则将父节点的children
属性赋值为空数组。
代码示例
// 获取菜单项
$menu_items = wp_get_nav_menu_items( 'main-menu' );
// 构建层级结构
$menu_tree = build_menu_tree( $menu_items );
// 输出菜单
function display_menu_tree( $menu_items ) {
if ( ! empty( $menu_items ) ) {
echo '<ul>';
foreach ( $menu_items as $item ) {
echo '<li><a href="' . esc_url( $item->url ) . '">' . esc_html( $item->title ) . '</a>';
if ( ! empty( $item->children ) ) {
display_menu_tree( $item->children );
}
echo '</li>';
}
echo '</ul>';
}
}
display_menu_tree( $menu_tree );
这段代码首先使用 wp_get_nav_menu_items()
函数获取名为 "main-menu" 的菜单项。 然后使用 build_menu_tree()
函数构建菜单项的层级结构。 最后使用 display_menu_tree()
函数递归地输出菜单项,生成 HTML 代码。
WP_Post
对象的重要属性
在构建层级结构和输出菜单时,我们需要用到 WP_Post
对象的以下属性:
属性名 | 描述 |
---|---|
ID |
菜单项的 ID。 |
title |
菜单项的标题。 |
url |
菜单项的 URL。 |
menu_order |
菜单项的排序顺序。 |
menu_item_parent |
菜单项的父菜单项 ID。 如果为 0,则表示该菜单项是顶级菜单项。 |
object |
菜单项指向的对象类型,例如 ‘page’、’post’、’category’ 等。 |
object_id |
菜单项指向的对象的 ID。 例如,如果 object 为 ‘page’,则 object_id 为页面的 ID。 |
type |
菜单项的类型,例如 ‘post_type’、’taxonomy’、’custom’ 等。 |
type_label |
菜单项类型的标签,例如 ‘页面’、’文章’、’分类目录’ 等。 |
target |
链接的目标属性,例如 ‘_blank’,用于在新窗口中打开链接。 |
attr_title |
链接的 title 属性,用于鼠标悬停时显示提示信息。 |
classes |
链接的 CSS 类名,可以添加自定义的 CSS 样式。 |
xfn |
链接的 xfn 属性,用于描述链接的关系。 |
总结
wp_get_nav_menu_items()
函数是 WordPress 获取菜单项的核心函数。 它通过构建 WP_Query
查询语句,从数据库中获取菜单项,并返回一个包含 WP_Post
对象的数组。 要构建菜单项的层级结构,需要遍历菜单项数组,根据 menu_item_parent
属性将菜单项组织成树形结构。 然后,可以递归地输出菜单项,生成 HTML 代码。
额外提示
- 缓存:WordPress 会缓存菜单项,以提高性能。 如果你修改了菜单,但页面没有立即更新,可以尝试清除 WordPress 的缓存。
- 过滤器:
wp_get_nav_menu_items()
函数提供了多个过滤器,允许你自定义菜单项的查询和输出。 例如,可以使用wp_nav_menu_objects
过滤器来修改菜单项的属性。
结束语
好了,今天的讲座就到这里。 希望通过今天的讲解,大家对 wp_get_nav_menu_items()
函数有了更深入的了解。 记住,理解底层原理,才能更好地驾驭 WordPress!
下次再见!