WordPress源码深度解析之:`WordPress`的`custom taxonomy`:`register_taxonomy()`的底层实现。

大家好,今天咱们来聊聊WordPress的“自立门户”——自定义分类法(Custom Taxonomy)

嗨,各位!今天咱们不搞虚的,直接撸起袖子,聊聊WordPress里那些让你的网站内容井井有条的“小帮手”——自定义分类法(Custom Taxonomy)。说白了,就是除了WordPress自带的分类(Category)和标签(Tag)之外,你还可以自己定义一套分类体系,让你的内容组织得更个性化,更符合你的需求。

今天咱们就来扒一扒这个自定义分类法的核心函数:register_taxonomy() 的底层实现,看看它到底是怎么工作的。我会尽量用大白话,配合代码,让大家都能听明白。

一、啥是自定义分类法?为啥要用它?

想象一下,你开了一家卖书的网站。WordPress自带的分类可能只有“小说”、“散文”、“诗歌”这些。但你还想按“作者国籍”、“出版年份”、“适合年龄”来分类,这时候,自定义分类法就派上大用场了。

简单来说,自定义分类法就是你可以根据自己的需求,创建自己专属的分类方式。

为啥要用它?

  • 更灵活的内容组织: 更好地整理和呈现你的内容,让用户更容易找到他们想要的东西。
  • 更好的SEO: 更精确的分类,有助于搜索引擎更好地理解你的网站内容。
  • 更个性化的用户体验: 针对不同的分类,可以设计不同的页面模板,提供更个性化的用户体验。

二、register_taxonomy() 函数:分类法的“户口登记处”

想要创建一个自定义分类法,就得用到 register_taxonomy() 这个函数。你可以把它想象成一个“户口登记处”,你得告诉它你想创建什么类型的分类法,叫什么名字,跟哪些文章类型有关联等等。

函数原型:

register_taxonomy(
    string   $taxonomy,
    string|string[] $object_type,
    array|string[] $args = array()
);

参数解释:

参数 类型 描述
$taxonomy string (必须) 分类法的名称(slug)。必须是小写字母,可以使用下划线,但不能使用空格。这个名称将在URL中使用,所以要慎重选择。
$object_type string/array (必须) 与分类法关联的文章类型。可以是单个文章类型(如 ‘post’),也可以是文章类型数组(如 array( 'post', 'page' ))。
$args array (可选) 一个包含各种参数的数组,用于配置分类法的行为和外观。这个数组非常重要,它可以让你控制分类法的各种属性,比如是否可以分层(像分类一样,可以有父分类和子分类),是否显示在管理后台等等。

举个栗子:

function register_book_genre_taxonomy() {
    $labels = array(
        'name'                       => _x( '图书类型', 'Taxonomy General Name', 'text_domain' ),
        'singular_name'              => _x( '图书类型', 'Taxonomy Singular Name', 'text_domain' ),
        'menu_name'                  => __( '图书类型', 'text_domain' ),
        'all_items'                  => __( '所有类型', 'text_domain' ),
        'parent_item'                => __( '父类型', 'text_domain' ),
        'parent_item_colon'          => __( '父类型:', 'text_domain' ),
        'new_item_name'              => __( '新类型名称', 'text_domain' ),
        'add_new_item'             => __( '添加新类型', 'text_domain' ),
        'edit_item'                => __( '编辑类型', 'text_domain' ),
        'update_item'              => __( '更新类型', 'text_domain' ),
        'view_item'                => __( '查看类型', 'text_domain' ),
        'separate_items_with_commas' => __( '用逗号分隔类型', 'text_domain' ),
        'add_or_remove_items'        => __( '添加或移除类型', 'text_domain' ),
        'choose_from_most_used'      => __( '从常用类型中选择', 'text_domain' ),
        'popular_items'              => __( '热门类型', 'text_domain' ),
        'search_items'               => __( '搜索类型', 'text_domain' ),
        'not_found'                  => __( '未找到', 'text_domain' ),
        'no_terms'                   => __( '没有类型', 'text_domain' ),
        'items_list'                 => __( '类型列表', 'text_domain' ),
        'items_list_navigation'      => __( '类型列表导航', 'text_domain' ),
    );
    $args = array(
        'labels'                     => $labels,
        'hierarchical'               => true, // 像分类一样,可以有父子关系
        'public'                     => true, // 是否公开
        'show_ui'                    => true, // 是否在管理后台显示
        'show_admin_column'          => true, // 是否在文章列表页显示分类列
        'show_in_nav_menus'          => true, // 是否在导航菜单中显示
        'show_tagcloud'              => true, // 是否在标签云中显示
        'query_var'                  => true, // 是否允许通过URL查询
        'rewrite'                    => array( 'slug' => 'book-genre' ), // URL重写规则
    );
    register_taxonomy( 'book_genre', 'book', $args );
}
add_action( 'init', 'register_book_genre_taxonomy', 0 );

// 注册一个名为 "book" 的文章类型 (Post Type)
function create_book_post_type() {
  $labels = array(
    'name'               => _x( '书籍', 'post type general name', 'your-plugin-textdomain' ),
    'singular_name'      => _x( '书籍', 'post type singular name', 'your-plugin-textdomain' ),
    'menu_name'          => _x( '书籍', 'admin menu', 'your-plugin-textdomain' ),
    'name_admin_bar'     => _x( '书籍', 'add new on admin bar', 'your-plugin-textdomain' ),
    'add_new'            => _x( '添加新书籍', 'book', 'your-plugin-textdomain' ),
    'add_new_item'       => __( '添加新书籍', 'your-plugin-textdomain' ),
    'new_item'           => __( '新书籍', 'your-plugin-textdomain' ),
    'edit_item'          => __( '编辑书籍', 'your-plugin-textdomain' ),
    'update_item'        => __( '更新书籍', 'your-plugin-textdomain' ),
    'view_item'          => __( '查看书籍', 'your-plugin-textdomain' ),
    'all_items'          => __( '所有书籍', 'your-plugin-textdomain' ),
    'search_items'       => __( '搜索书籍', 'your-plugin-textdomain' ),
    'parent_item_colon'  => __( '父书籍:', 'your-plugin-textdomain' ),
    'not_found'          => __( '未找到书籍.', 'your-plugin-textdomain' ),
    'not_found_in_trash' => __( '回收站中未找到书籍.', 'your-plugin-textdomain' )
  );

  $args = array(
    'labels'             => $labels,
    'public'             => true,
    'publicly_queryable' => true,
    'show_ui'            => true,
    'show_in_menu'       => true,
    'query_var'          => true,
    'rewrite'            => array( 'slug' => 'book' ),
    'capability_type'    => 'post',
    'has_archive'        => true,
    'hierarchical'       => false,
    'menu_position'      => null,
    'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
  );

  register_post_type( 'book', $args );
}
add_action( 'init', 'create_book_post_type', 0 );

这段代码做了什么?

  1. 定义了一个函数 register_book_genre_taxonomy() 这个函数负责注册我们的自定义分类法。
  2. 定义了 $labels 数组: 这个数组包含了分类法在管理后台显示的各种文字标签,比如“图书类型”、“添加新类型”等等。
  3. 定义了 $args 数组: 这个数组包含了分类法的各种配置选项,比如是否可以分层、是否公开、是否在管理后台显示等等。
  4. 调用 register_taxonomy() 函数: 这个函数才是真正注册分类法的关键。它接受三个参数:分类法的名称('book_genre')、文章类型('book'),以及配置选项($args)。
  5. 使用 add_action() 函数: 这个函数将 register_book_genre_taxonomy() 函数挂载到 init 钩子上,确保在WordPress初始化时执行。

这样,我们就成功创建了一个名为 "图书类型" (book_genre) 的自定义分类法,它与 "书籍" (book) 这种文章类型相关联。

三、register_taxonomy() 的底层实现:一层层拨开它的心

现在,咱们来深入了解一下 register_taxonomy() 函数的底层实现。为了方便理解,我将它分解成几个关键步骤,并用伪代码进行解释。

1. 参数校验:

// 伪代码:register_taxonomy() 函数内部

// 1. 检查参数是否合法
if ( empty( $taxonomy ) || ! is_string( $taxonomy ) ) {
    // 抛出错误或返回 false
    return new WP_Error( 'invalid_taxonomy', __( '分类法名称必须是一个非空的字符串。' ) );
}

if ( empty( $object_type ) ) {
    // 抛出错误或返回 false
    return new WP_Error( 'invalid_object_type', __( '必须指定分类法关联的文章类型。' ) );
}

register_taxonomy() 函数首先会检查你传入的参数是否合法。比如,分类法的名称不能为空,必须是一个字符串;必须指定与分类法关联的文章类型等等。如果参数不合法,函数会返回一个错误。

2. 初始化全局变量:

// 伪代码:register_taxonomy() 函数内部

global $wp_taxonomies;

if ( ! is_array( $wp_taxonomies ) ) {
    $wp_taxonomies = array();
}

register_taxonomy() 函数会使用一个全局变量 $wp_taxonomies 来存储所有已注册的分类法。如果 $wp_taxonomies 变量还不存在,函数会将其初始化为一个空数组。

3. 创建分类法对象:

// 伪代码:register_taxonomy() 函数内部

// 3. 创建一个新的 WP_Taxonomy 对象
$taxonomy_object = new WP_Taxonomy( $taxonomy, $object_type, $args );

// 如果 WP_Taxonomy 类不存在,则返回 WP_Error
if ( is_wp_error( $taxonomy_object ) ) {
    return $taxonomy_object;
}

register_taxonomy() 函数会创建一个 WP_Taxonomy 类的实例,这个实例代表了一个具体的分类法。WP_Taxonomy 类会根据你传入的参数,初始化分类法的各种属性,比如名称、文章类型、配置选项等等。

4. 存储分类法对象:

// 伪代码:register_taxonomy() 函数内部

// 4. 将分类法对象存储到全局变量 $wp_taxonomies 中
$wp_taxonomies[ $taxonomy ] = $taxonomy_object;

register_taxonomy() 函数会将创建好的 WP_Taxonomy 对象存储到全局变量 $wp_taxonomies 中。这样,WordPress就可以在需要的时候,通过分类法的名称来访问到这个分类法对象。

5. 关联文章类型:

// 伪代码:register_taxonomy() 函数内部

// 5. 将分类法与文章类型关联起来
foreach ( (array) $object_type as $post_type ) {
    register_taxonomy_for_object_type( $taxonomy, $post_type );
}

register_taxonomy() 函数会将分类法与指定的文章类型关联起来。这意味着,当你创建或编辑这些文章类型的文章时,就可以选择使用这个分类法进行分类。

6. 触发钩子:

// 伪代码:register_taxonomy() 函数内部

// 6. 触发一个钩子,允许其他插件或主题修改分类法对象
do_action( 'registered_taxonomy', $taxonomy, $object_type, $taxonomy_object );

register_taxonomy() 函数会触发一个名为 registered_taxonomy 的钩子。这个钩子允许其他插件或主题在分类法注册完成后,对其进行修改或扩展。

简化版的流程图:

+-----------------------+     +-----------------------+     +-----------------------+     +-----------------------+     +-----------------------+     +-----------------------+
|     参数校验          | --> |   初始化全局变量     | --> |   创建分类法对象      | --> |   存储分类法对象      | --> |   关联文章类型        | --> |     触发钩子          |
+-----------------------+     +-----------------------+     +-----------------------+     +-----------------------+     +-----------------------+     +-----------------------+

四、WP_Taxonomy 类:分类法的“灵魂”

刚才我们提到了 WP_Taxonomy 类,这个类是自定义分类法的核心。它定义了分类法的各种属性和方法。

WP_Taxonomy 类的重要属性:

属性 类型 描述
$name string 分类法的名称(slug)。
$object_type array 与分类法关联的文章类型数组。
$label string 分类法的通用标签,用于在管理后台显示。
$labels array 包含各种文字标签的数组,用于配置分类法在管理后台的显示。
$description string 分类法的描述。
$hierarchical bool 是否可以分层(像分类一样,可以有父分类和子分类)。
$public bool 是否公开。
$show_ui bool 是否在管理后台显示。
$show_admin_column bool 是否在文章列表页显示分类列。
$rewrite array URL重写规则。
$query_var string/bool 是否允许通过URL查询。

WP_Taxonomy 类的重要方法:

虽然我们无法直接访问 WP_Taxonomy 类的源码(因为它是WordPress内核的一部分),但我们可以通过一些WordPress函数来间接访问和修改它的属性。

  • get_taxonomy( $taxonomy ): 获取一个已注册的分类法对象。
  • get_taxonomy_labels( $taxonomy ): 获取分类法的标签数组。
  • taxonomy_exists( $taxonomy ): 检查分类法是否存在。

五、一些需要注意的点

  • 分类法的名称($taxonomy): 必须是小写字母,可以使用下划线,但不能使用空格。
  • 文章类型($object_type): 可以是WordPress自带的文章类型(如 postpage),也可以是你自己定义的文章类型。
  • 'rewrite' 参数: 控制分类法URL的结构。如果你修改了这个参数,需要刷新WordPress的固定链接设置。
  • 'labels' 参数: 非常重要,它决定了分类法在管理后台的显示方式。

六、总结

今天咱们一起深入了解了WordPress自定义分类法的核心函数 register_taxonomy() 的底层实现。虽然代码看起来有点复杂,但只要理解了它的基本原理,就能更好地使用自定义分类法,让你的网站内容组织得更井井有条。

记住,自定义分类法是WordPress提供的一个非常强大的工具,它可以让你根据自己的需求,创建自己专属的分类体系,从而更好地管理和呈现你的内容。

希望今天的讲解对大家有所帮助!下次有机会再和大家聊聊WordPress的其他技术细节。 各位,下次见!

发表回复

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