大家好,今天咱们来聊聊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 );
这段代码做了什么?
- 定义了一个函数
register_book_genre_taxonomy()
: 这个函数负责注册我们的自定义分类法。 - 定义了
$labels
数组: 这个数组包含了分类法在管理后台显示的各种文字标签,比如“图书类型”、“添加新类型”等等。 - 定义了
$args
数组: 这个数组包含了分类法的各种配置选项,比如是否可以分层、是否公开、是否在管理后台显示等等。 - 调用
register_taxonomy()
函数: 这个函数才是真正注册分类法的关键。它接受三个参数:分类法的名称('book_genre'
)、文章类型('book'
),以及配置选项($args
)。 - 使用
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自带的文章类型(如post
、page
),也可以是你自己定义的文章类型。 'rewrite'
参数: 控制分类法URL的结构。如果你修改了这个参数,需要刷新WordPress的固定链接设置。'labels'
参数: 非常重要,它决定了分类法在管理后台的显示方式。
六、总结
今天咱们一起深入了解了WordPress自定义分类法的核心函数 register_taxonomy()
的底层实现。虽然代码看起来有点复杂,但只要理解了它的基本原理,就能更好地使用自定义分类法,让你的网站内容组织得更井井有条。
记住,自定义分类法是WordPress提供的一个非常强大的工具,它可以让你根据自己的需求,创建自己专属的分类体系,从而更好地管理和呈现你的内容。
希望今天的讲解对大家有所帮助!下次有机会再和大家聊聊WordPress的其他技术细节。 各位,下次见!