分析 WordPress `wp_get_nav_menu_items()` 函数的源码:如何查询并返回导航菜单项的列表。

大家好,欢迎来到今天的“WordPress源码解密”讲座,我是你们的老朋友,代码界的段子手。今天咱们要聊的,是WordPress里一个非常重要的函数:wp_get_nav_menu_items()。 别看名字这么长,其实它干的事情很简单,就是负责把你的导航菜单,变成一列可以用的数据。

准备好了吗? 咱们开始!

一、 前戏: wp_get_nav_menu_items() 是个啥?

简单来说,wp_get_nav_menu_items() 函数就是用来从数据库里捞出导航菜单项(menu items)的。这些菜单项包括链接的标题、链接的地址、菜单排序、所属菜单等等信息。 你可以把它想象成一个经验丰富的服务员,你告诉他你要哪个菜单(通过菜单ID、菜单名称或者菜单对象),他就能给你把这个菜单上的所有菜品(菜单项)都端上来。

二、 深入源码: 剥开 wp-includes/nav-menu.php 的神秘面纱

让我们打开wp-includes/nav-menu.php这个文件,找到wp_get_nav_menu_items() 函数。别害怕,源码其实没那么可怕,咱们一行一行地过。

function wp_get_nav_menu_items( $menu, $args = array() ) {
    /**
     * Fires before retrieving navigation menu items.
     *
     * @since 3.0.0
     *
     * @param mixed $menu Menu ID, slug, name, or object.
     * @param array $args Array of get_terms() arguments.
     */
    do_action( 'wp_get_nav_menu_items', $menu, $args );

    $menu_object = wp_get_nav_menu_object( $menu );

    if ( ! $menu_object ) {
        return false;
    }

    $menu_items = wp_cache_get( 'nav_menu_items_' . $menu_object->term_id, 'nav_menu' );
    if ( false === $menu_items ) {
        $args = wp_parse_args( $args, array(
            'orderby' => 'menu_order,title',
            'order'   => 'ASC',
            'post_type' => 'nav_menu_item',
            'post_status' => 'publish',
            'output' => ARRAY_A, // 默认返回数组
            'output_key' => 'menu_order',
            'update_post_term_cache' => false,
        ) );

        $menu_items = get_posts( array_merge(
            $args,
            array(
                'numberposts' => -1,
                'tax_query' => array(
                    array(
                        'taxonomy' => 'nav_menu',
                        'field' => 'term_id',
                        'terms' => $menu_object->term_id,
                    ),
                ),
            )
        ) );

        wp_cache_set( 'nav_menu_items_' . $menu_object->term_id, $menu_items, 'nav_menu' );
    }

    /**
     * Filters the navigation menu items being returned.
     *
     * @since 3.0.0
     *
     * @param array   $menu_items The menu items, sorted by menu order.
     * @param stdClass $menu       The menu object.
     * @param array   $args       Array of get_terms() arguments.
     */
    return apply_filters( 'wp_get_nav_menu_items', $menu_items, $menu_object, $args );
}

三、 源码解剖: 一步一步理解代码逻辑

  1. do_action( 'wp_get_nav_menu_items', $menu, $args );

    • 这是一个钩子 (Action Hook)。 WordPress 允许开发者在函数执行的特定点插入自定义代码。 在这里,它允许你在函数开始执行前,执行一些操作,例如记录日志、修改参数等。
    • $menu$args 是传递给钩子的参数,分别是菜单的标识符(ID、名称或对象)和参数数组。
  2. $menu_object = wp_get_nav_menu_object( $menu );

    • 这个函数负责获取菜单对象。 你可以传递菜单的ID、slug或者菜单对象本身。 如果传递的是ID或者slug,它会从数据库中查找对应的菜单对象。
    • wp_get_nav_menu_object() 函数返回的是一个 stdClass 对象,包含了菜单的各种信息,例如菜单ID、菜单名称、菜单slug等等。
  3. if ( ! $menu_object ) { return false; }

    • 如果 $menu_object 为空,说明找不到对应的菜单,函数直接返回 false。 这是个很重要的检查,防止后续代码出现错误。
  4. $menu_items = wp_cache_get( 'nav_menu_items_' . $menu_object->term_id, 'nav_menu' );

    • 这里用到了 WordPress 的缓存机制。 WordPress 会尝试从缓存中获取菜单项数据。 如果缓存中存在,则直接返回缓存的数据,避免重复查询数据库,提高性能。
    • wp_cache_get() 函数的第一个参数是缓存的 key,这里是 nav_menu_items_ 加上菜单的 ID。 第二个参数是缓存的 group,这里是 nav_menu
  5. if ( false === $menu_items ) { ... }

    • 如果缓存中没有菜单项数据,则执行 if 里面的代码,从数据库中查询。
  6. 参数解析 (重点!)

    • $args = wp_parse_args( $args, array( ... ) );
      • wp_parse_args() 函数用于合并用户传入的参数和默认参数。 这样可以保证所有的参数都有值,即使用户没有传递。
    • 默认参数:

      参数名 默认值 描述
      orderby 'menu_order,title' 排序方式。 默认先按照菜单顺序 (menu_order) 排序,如果菜单顺序相同,则按照标题 (title) 排序。
      order 'ASC' 排序顺序。 默认是升序 (ASC)。
      post_type 'nav_menu_item' 查询的 post type。 导航菜单项的 post type 都是 nav_menu_item
      post_status 'publish' 查询的 post status。 只查询已发布的菜单项。
      output ARRAY_A 返回的数据类型。 ARRAY_A 表示返回关联数组。
      output_key 'menu_order' 数组的 key。 默认使用菜单顺序作为数组的 key。
      update_post_term_cache false 是否更新文章和term的缓存。 这里设置为 false 可以提高性能。
  7. $menu_items = get_posts( array_merge( ... ) );

    • 这是最核心的一步,它使用 get_posts() 函数从数据库中查询菜单项。

    • array_merge() 函数用于合并参数数组。 它将用户传入的参数、默认参数和查询条件合并在一起。

    • 查询条件:

      参数名 描述
      numberposts -1 查询所有的菜单项。
      tax_query array(...) taxonomy 查询。 用于指定查询的 term 是属于哪个 taxonomy 的。 nav_menu 是菜单项所属的 taxonomy。
      taxonomy 'nav_menu' taxonomy 的名称。
      field 'term_id' 用于匹配 term 的字段。 这里使用 term 的 ID。
      terms $menu_object->term_id 要匹配的 term 的 ID。 也就是当前菜单的 ID。
    • get_posts() 函数返回一个 WP_Post 对象的数组,每个对象代表一个菜单项。

  8. wp_cache_set( 'nav_menu_items_' . $menu_object->term_id, $menu_items, 'nav_menu' );

    • 将查询到的菜单项数据存入缓存,以便下次使用。
  9. return apply_filters( 'wp_get_nav_menu_items', $menu_items, $menu_object, $args );

    • 这是一个过滤器 (Filter Hook)。 允许开发者修改函数返回的菜单项数据。
    • $menu_items 是查询到的菜单项数组,$menu_object 是菜单对象,$args 是参数数组。

四、 实战演练: 如何使用 wp_get_nav_menu_items()

现在,让我们看看如何在实际代码中使用 wp_get_nav_menu_items() 函数。

<?php
// 获取 ID 为 2 的菜单项
$menu_items = wp_get_nav_menu_items( 2 );

if ( $menu_items ) {
    foreach ( $menu_items as $menu_item ) {
        $title = $menu_item->title;
        $url   = $menu_item->url;
        echo '<a href="' . esc_url( $url ) . '">' . esc_html( $title ) . '</a><br>';
    }
} else {
    echo '找不到菜单';
}
?>

这段代码首先调用 wp_get_nav_menu_items( 2 ) 获取 ID 为 2 的菜单项。 然后,它遍历菜单项数组,输出每个菜单项的标题和链接地址。 注意,这里使用了 esc_url()esc_html() 函数来对 URL 和 HTML 进行转义,以防止 XSS 攻击。

五、 深入挖掘: WP_Post 对象里的乾坤

wp_get_nav_menu_items() 返回的菜单项,实际上是 WP_Post 对象的数组。 每个 WP_Post 对象都包含了菜单项的各种信息。 让我们看看 WP_Post 对象里都有哪些常用的属性:

属性名 描述
ID 菜单项的 ID。
post_title 菜单项的标题。
post_name 菜单项的 slug。
post_content 菜单项的内容。 一般为空。
post_excerpt 菜单项的摘要。 一般为空。
menu_order 菜单项的顺序。
post_parent 父菜单项的 ID。 如果是顶级菜单项,则为 0。
_menu_item_object 菜单项指向的对象类型。 例如,pagepostcategory 等。
_menu_item_object_id 菜单项指向的对象的 ID。 例如,如果是指向一个页面,则这里是页面的 ID。
_menu_item_type 菜单项的类型。 例如,post_typetaxonomycustom 等。 post_type 表示指向一个文章类型,taxonomy 表示指向一个分类法,custom 表示自定义链接。
_menu_item_url 菜单项的 URL。
_menu_item_target 链接的目标窗口。 例如,_blank 表示在新窗口中打开链接。
_menu_item_classes 菜单项的 CSS 类。
_menu_item_xfn 链接的关系属性 (XFN)。 例如,friendfamily 等。

六、 高级用法: 玩转参数和过滤器

wp_get_nav_menu_items() 函数提供了很多参数和过滤器,可以让你灵活地控制菜单项的查询和输出。

1. 修改参数:

你可以通过 $args 参数来修改查询条件。 例如,你可以只查询特定状态的菜单项:

<?php
$args = array(
    'post_status' => 'draft', // 只查询草稿状态的菜单项
);
$menu_items = wp_get_nav_menu_items( 2, $args );
?>

2. 使用过滤器:

你可以使用 wp_get_nav_menu_items 过滤器来修改菜单项数据。 例如,你可以给每个菜单项添加一个自定义的 CSS 类:

<?php
function my_custom_menu_item_class( $menu_items, $menu_object, $args ) {
    foreach ( $menu_items as $menu_item ) {
        $menu_item->classes[] = 'my-custom-class';
    }
    return $menu_items;
}
add_filter( 'wp_get_nav_menu_items', 'my_custom_menu_item_class', 10, 3 );
?>

这段代码定义了一个名为 my_custom_menu_item_class 的函数,它接受三个参数:菜单项数组、菜单对象和参数数组。 该函数遍历菜单项数组,给每个菜单项的 classes 属性添加一个 my-custom-class 类。 然后,它使用 add_filter() 函数将该函数注册到 wp_get_nav_menu_items 过滤器上。 这样,每次调用 wp_get_nav_menu_items() 函数时,都会执行 my_custom_menu_item_class 函数,给每个菜单项添加自定义的 CSS 类。

七、 注意事项: 避开那些坑

  • 缓存: wp_get_nav_menu_items() 函数使用了缓存机制。 如果你修改了菜单项,需要清除缓存才能看到效果。 你可以使用 WordPress 的缓存插件,或者手动清除缓存。
  • 性能: 如果你的菜单项非常多,查询菜单项可能会影响性能。 你可以考虑使用缓存插件,或者优化数据库查询。
  • 安全: 务必对 URL 和 HTML 进行转义,以防止 XSS 攻击。 使用 esc_url()esc_html() 函数。

八、总结: 掌握 wp_get_nav_menu_items(),你就是导航菜单大师

wp_get_nav_menu_items() 函数是 WordPress 中一个非常重要的函数,它负责从数据库中查询导航菜单项。 通过理解它的源码和用法,你可以灵活地控制菜单项的查询和输出,打造出各种各样的导航菜单。

今天的讲座就到这里了。 希望大家能够掌握 wp_get_nav_menu_items() 函数的用法,成为真正的导航菜单大师! 下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注