各位观众老爷们,晚上好!我是你们的老朋友,今天咱们来聊聊 WordPress 里的一个“重量级”选手:wp_set_post_terms()
函数。别被它长长的名字吓到,其实理解了它的核心逻辑,你会发现它也没那么可怕。
今天咱们就来扒一扒它的源码,看看它是如何给文章设置分类术语,又是如何更新那些让人头疼的计数器的。准备好了吗?咱们开始!
开场白:术语分类的重要性
在WordPress的世界里,文章(Post)就像是咱们辛辛苦苦码出来的文字结晶,而分类术语(Taxonomy Terms),比如分类(Category)和标签(Tag),则是用来组织和管理这些文章的利器。想象一下,如果没有分类,所有的文章都堆在一起,那岂不是乱成一锅粥?
wp_set_post_terms()
函数就是负责把文章和这些分类术语联系起来的关键。它能让咱们轻松地给文章添加、删除或更新分类和标签。
源码解析:wp_set_post_terms()
的真面目
好,废话不多说,直接上代码!不过,为了方便理解,咱们会把它拆解成几个小模块来讲解。
function wp_set_post_terms( $post_id, $terms, $taxonomy = 'post_tag', $append = false ) {
global $wpdb;
$post_id = (int) $post_id;
if ( empty( $taxonomy ) ) {
return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) );
}
$taxonomy = sanitize_key( $taxonomy );
$term_taxonomy_ids = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'tt_ids' ) );
if ( is_wp_error( $term_taxonomy_ids ) ) {
return $term_taxonomy_ids;
}
$term_taxonomy_ids = array_map( 'intval', $term_taxonomy_ids );
if ( ! is_array( $terms ) ) {
$terms = array( $terms );
}
$terms = array_filter( $terms );
if ( empty( $terms ) && ! empty( $term_taxonomy_ids ) ) {
return wp_remove_object_terms( $post_id, $term_taxonomy_ids, $taxonomy );
}
if ( ! empty( $terms ) ) {
$new_tt_ids = wp_create_term_taxonomies( $terms, $taxonomy );
if ( is_wp_error( $new_tt_ids ) ) {
return $new_tt_ids;
}
if ( ! is_array( $new_tt_ids ) ) {
return new WP_Error( 'unexpected_output', __( 'Unexpected output.' ) );
}
$tt_ids = array_values( $new_tt_ids );
} else {
$tt_ids = array();
}
if ( $append ) {
$tt_ids = array_merge( $term_taxonomy_ids, $tt_ids );
}
$tt_ids = array_map( 'intval', $tt_ids );
$tt_ids = array_unique( $tt_ids );
$tt_ids = array_diff( $tt_ids, $term_taxonomy_ids );
$tt_ids = array_map( 'intval', $tt_ids );
$term_taxonomy_ids = array_map( 'intval', $term_taxonomy_ids );
$insertions = array();
$deleteions = array();
foreach ( (array) $tt_ids as $tt_id ) {
$insertions[] = $wpdb->prepare( "(%d, %d, %s)", $post_id, $tt_id, $taxonomy );
}
if ( ! empty( $insertions ) ) {
$query = "INSERT INTO {$wpdb->term_relationships} (object_id, term_taxonomy_id, term_order) VALUES " . implode( ',', $insertions );
$wpdb->query( $query );
}
$deleteions = array_diff( $term_taxonomy_ids, array_map( 'intval', $tt_ids ) );
if ( ! empty( $deleteions ) ) {
$delete_tt_ids = implode( ',', $deleteions );
$query = "DELETE FROM {$wpdb->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($delete_tt_ids)";
$wpdb->query( $wpdb->prepare( $query, $post_id ) );
}
wp_cache_delete( $post_id, $taxonomy . '_relationships' );
do_action( 'set_object_terms', $post_id, $terms, $taxonomy, $append, $old_tt_ids );
wp_update_term_count_now( array_merge( (array) $deleteions, (array) $tt_ids ), $taxonomy );
do_action( 'edited_' . $taxonomy, $post_id );
return array_map( 'intval', array_values( $tt_ids ) );
}
哇,代码真多!别慌,咱们一步一步来。
1. 参数校验和准备工作
$post_id = (int) $post_id;
if ( empty( $taxonomy ) ) {
return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) );
}
$taxonomy = sanitize_key( $taxonomy );
这一段主要是做一些参数的校验,确保传入的参数是有效的。
$post_id = (int) $post_id;
:将$post_id
强制转换为整数,确保它是文章的 ID。if ( empty( $taxonomy ) ) { ... }
:检查$taxonomy
是否为空,如果为空,则返回一个 WP_Error 对象,提示 taxonomy 无效。$taxonomy = sanitize_key( $taxonomy );
:使用sanitize_key()
函数对$taxonomy
进行清理,确保它是一个安全的键名。
2. 获取现有的 Term Taxonomy IDs
$term_taxonomy_ids = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'tt_ids' ) );
if ( is_wp_error( $term_taxonomy_ids ) ) {
return $term_taxonomy_ids;
}
$term_taxonomy_ids = array_map( 'intval', $term_taxonomy_ids );
这一步是为了获取文章当前已经关联的 term taxonomy IDs。
wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'tt_ids' ) );
:调用wp_get_object_terms()
函数,传入$post_id
、$taxonomy
和一个选项数组,'fields' => 'tt_ids'
表示只获取 term taxonomy IDs。if ( is_wp_error( $term_taxonomy_ids ) ) { ... }
:检查wp_get_object_terms()
的返回值是否是一个 WP_Error 对象,如果是,则直接返回该错误。$term_taxonomy_ids = array_map( 'intval', $term_taxonomy_ids );
:使用array_map()
函数将$term_taxonomy_ids
数组中的所有元素转换为整数。
3. 处理传入的 Terms
if ( ! is_array( $terms ) ) {
$terms = array( $terms );
}
$terms = array_filter( $terms );
这里处理传入的 $terms
参数,确保它是一个数组,并且数组中的元素都是有效的。
if ( ! is_array( $terms ) ) { ... }
:如果$terms
不是一个数组,则将其转换为一个包含$terms
的数组。$terms = array_filter( $terms );
:使用array_filter()
函数过滤$terms
数组,移除所有空值(例如null
、false
、''
、0
等)。
4. 如果没有 Terms 且文章有现有的 Term Taxonomy IDs,则删除它们
if ( empty( $terms ) && ! empty( $term_taxonomy_ids ) ) {
return wp_remove_object_terms( $post_id, $term_taxonomy_ids, $taxonomy );
}
如果传入的 $terms
为空,并且文章已经关联了一些 term taxonomy IDs,则调用 wp_remove_object_terms()
函数删除这些关联。
5. 创建或获取 Term Taxonomy IDs
if ( ! empty( $terms ) ) {
$new_tt_ids = wp_create_term_taxonomies( $terms, $taxonomy );
if ( is_wp_error( $new_tt_ids ) ) {
return $new_tt_ids;
}
if ( ! is_array( $new_tt_ids ) ) {
return new WP_Error( 'unexpected_output', __( 'Unexpected output.' ) );
}
$tt_ids = array_values( $new_tt_ids );
} else {
$tt_ids = array();
}
如果传入的 $terms
不为空,则需要创建或获取对应的 term taxonomy IDs。
$new_tt_ids = wp_create_term_taxonomies( $terms, $taxonomy );
:调用wp_create_term_taxonomies()
函数,传入$terms
和$taxonomy
,该函数会创建或获取对应的 term taxonomy IDs。if ( is_wp_error( $new_tt_ids ) ) { ... }
:检查wp_create_term_taxonomies()
的返回值是否是一个 WP_Error 对象,如果是,则直接返回该错误。if ( ! is_array( $new_tt_ids ) ) { ... }
:检查wp_create_term_taxonomies()
的返回值是否是一个数组,如果不是,则返回一个 WP_Error 对象。$tt_ids = array_values( $new_tt_ids );
:使用array_values()
函数获取$new_tt_ids
数组的值,并将其赋值给$tt_ids
。
6. 合并 Term Taxonomy IDs (如果 $append
为 true)
if ( $append ) {
$tt_ids = array_merge( $term_taxonomy_ids, $tt_ids );
}
如果 $append
参数为 true,则将现有的 term taxonomy IDs 和新的 term taxonomy IDs 合并。
7. 清理和准备 Term Taxonomy IDs
$tt_ids = array_map( 'intval', $tt_ids );
$tt_ids = array_unique( $tt_ids );
$tt_ids = array_diff( $tt_ids, $term_taxonomy_ids );
$tt_ids = array_map( 'intval', $tt_ids );
$term_taxonomy_ids = array_map( 'intval', $term_taxonomy_ids );
这一步对 $tt_ids
进行清理,确保其中包含的是唯一的、新增的 term taxonomy IDs。
$tt_ids = array_map( 'intval', $tt_ids );
:将$tt_ids
数组中的所有元素转换为整数。$tt_ids = array_unique( $tt_ids );
:使用array_unique()
函数移除$tt_ids
数组中的重复元素。$tt_ids = array_diff( $tt_ids, $term_taxonomy_ids );
:使用array_diff()
函数移除$tt_ids
数组中已经存在的 term taxonomy IDs,只保留新增的 IDs。- 再次使用
array_map
将$tt_ids
和$term_taxonomy_ids
数组中的所有元素转换为整数。
8. 插入新的 Term Relationships
$insertions = array();
$deleteions = array();
foreach ( (array) $tt_ids as $tt_id ) {
$insertions[] = $wpdb->prepare( "(%d, %d, %s)", $post_id, $tt_id, $taxonomy );
}
if ( ! empty( $insertions ) ) {
$query = "INSERT INTO {$wpdb->term_relationships} (object_id, term_taxonomy_id, term_order) VALUES " . implode( ',', $insertions );
$wpdb->query( $query );
}
这一步将新的 term taxonomy IDs 插入到 wp_term_relationships
表中,建立文章和 term taxonomy 之间的关联。
$insertions = array();
:初始化一个空数组$insertions
,用于存储要插入的 SQL 语句片段。foreach ( (array) $tt_ids as $tt_id ) { ... }
:遍历$tt_ids
数组,为每个 term taxonomy ID 构建一个 SQL 语句片段。$wpdb->prepare( "(%d, %d, %s)", $post_id, $tt_id, $taxonomy );
:使用$wpdb->prepare()
函数构建一个安全的 SQL 语句片段,防止 SQL 注入。if ( ! empty( $insertions ) ) { ... }
:如果$insertions
数组不为空,则执行插入操作。$query = "INSERT INTO {$wpdb->term_relationships} (object_id, term_taxonomy_id, term_order) VALUES " . implode( ',', $insertions );
:将$insertions
数组中的所有 SQL 语句片段连接成一个完整的 INSERT 语句。$wpdb->query( $query );
:执行 SQL 查询,将新的 term relationships 插入到wp_term_relationships
表中。
9. 删除不再需要的 Term Relationships
$deleteions = array_diff( $term_taxonomy_ids, array_map( 'intval', $tt_ids ) );
if ( ! empty( $deleteions ) ) {
$delete_tt_ids = implode( ',', $deleteions );
$query = "DELETE FROM {$wpdb->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($delete_tt_ids)";
$wpdb->query( $wpdb->prepare( $query, $post_id ) );
}
这一步删除文章不再需要的 term relationships。
$deleteions = array_diff( $term_taxonomy_ids, array_map( 'intval', $tt_ids ) );
:使用array_diff()
函数计算需要删除的 term taxonomy IDs,即存在于$term_taxonomy_ids
中,但不存在于$tt_ids
中的 IDs。if ( ! empty( $deleteions ) ) { ... }
:如果$deleteions
数组不为空,则执行删除操作。$delete_tt_ids = implode( ',', $deleteions );
:将$deleteions
数组中的所有 term taxonomy IDs 连接成一个字符串,用逗号分隔。$query = "DELETE FROM {$wpdb->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($delete_tt_ids)";
:构建一个 DELETE 语句,删除文章不再需要的 term relationships。$wpdb->query( $wpdb->prepare( $query, $post_id ) );
:执行 SQL 查询,删除不再需要的 term relationships。
10. 清理缓存
wp_cache_delete( $post_id, $taxonomy . '_relationships' );
删除与文章和 taxonomy 相关的缓存,确保数据是最新的。
11. 触发 Actions
do_action( 'set_object_terms', $post_id, $terms, $taxonomy, $append, $old_tt_ids );
wp_update_term_count_now( array_merge( (array) $deleteions, (array) $tt_ids ), $taxonomy );
do_action( 'edited_' . $taxonomy, $post_id );
触发一些 actions,允许其他插件或主题对文章的 term relationships 进行自定义操作。
do_action( 'set_object_terms', $post_id, $terms, $taxonomy, $append, $old_tt_ids );
:触发set_object_terms
action,传递文章 ID、terms、taxonomy、append 和旧的 term taxonomy IDs。wp_update_term_count_now( array_merge( (array) $deleteions, (array) $tt_ids ), $taxonomy );
:调用wp_update_term_count_now()
函数,更新 taxonomy 的计数器。这个函数可是非常重要,它负责更新每个分类术语下文章的数量。do_action( 'edited_' . $taxonomy, $post_id );
:触发edited_{$taxonomy}
action,传递文章 ID。
12. 返回结果
return array_map( 'intval', array_values( $tt_ids ) );
返回新的 term taxonomy IDs 数组。
wp_update_term_count_now()
:幕后英雄
咱们再来重点聊聊 wp_update_term_count_now()
这个函数,它可是维护分类术语计数的关键。
function wp_update_term_count_now( $terms, $taxonomy, $do_deferred = false ) {
global $wpdb;
$terms = array_map( 'intval', (array) $terms );
$taxonomy = sanitize_key( $taxonomy );
foreach ( (array) $terms as $term ) {
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->term_relationships} WHERE term_taxonomy_id = %d", $term ) );
$wpdb->update( $wpdb->term_taxonomy, array( 'count' => $count ), array( 'term_taxonomy_id' => $term ) );
wp_cache_delete( $term, $taxonomy . '_term_taxonomy' );
}
wp_cache_delete( 'all_terms', $taxonomy );
do_action( 'edited_term_taxonomy', $term, $taxonomy );
}
这个函数接收一个 term taxonomy IDs 数组和一个 taxonomy 名称,然后遍历每个 term taxonomy ID,统计与其关联的文章数量,并将结果更新到 wp_term_taxonomy
表中。
代码分解:
-
函数
wp_update_term_count_now
接收三个参数:$terms
(要更新计数的术语ID数组),$taxonomy
(术语的分类法名称),以及$do_deferred
(一个布尔值,决定是否延迟计数更新,默认false
)。 -
$terms = array_map( 'intval', (array) $terms );
:确保$terms
是一个整数数组。 -
$taxonomy = sanitize_key( $taxonomy );
:确保$taxonomy
是一个有效的键。 -
循环遍历
$terms
数组:-
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->term_relationships} WHERE term_taxonomy_id = %d", $term ) );
:使用SQL查询从wp_term_relationships
表中获取与当前术语ID相关的文章数量。$wpdb->prepare
用于安全地构建SQL查询,防止SQL注入。 -
$wpdb->update( $wpdb->term_taxonomy, array( 'count' => $count ), array( 'term_taxonomy_id' => $term ) );
:使用SQL更新wp_term_taxonomy
表中对应术语ID的count
列。 -
wp_cache_delete( $term, $taxonomy . '_term_taxonomy' );
:删除与当前术语相关的缓存,以确保下次访问时可以获取最新的数据。
-
-
wp_cache_delete( 'all_terms', $taxonomy );
:删除与指定分类法相关的所有术语的缓存。 -
do_action( 'edited_term_taxonomy', $term, $taxonomy );
:触发一个动作钩子edited_term_taxonomy
,允许其他插件或主题在术语分类法被编辑后执行自定义操作。
表格总结:wp_set_post_terms()
的核心步骤
为了让大家更清晰地理解 wp_set_post_terms()
函数的工作流程,我整理了一个表格:
步骤 | 描述 | 涉及的关键函数 |
---|---|---|
1. 参数校验 | 验证 $post_id 和 $taxonomy 参数的有效性。 |
sanitize_key() |
2. 获取现有 Term IDs | 获取文章当前已经关联的 term taxonomy IDs。 | wp_get_object_terms() |
3. 处理传入的 Terms | 确保传入的 $terms 参数是一个数组,并且数组中的元素都是有效的。 |
array_filter() |
4. 删除旧的 Terms | 如果 $terms 为空,且文章已经关联了一些 term taxonomy IDs,则删除这些关联。 |
wp_remove_object_terms() |
5. 创建/获取 Term IDs | 如果 $terms 不为空,则创建或获取对应的 term taxonomy IDs。 |
wp_create_term_taxonomies() |
6. 合并 Term IDs (append) | 如果 $append 为 true,则将现有的 term taxonomy IDs 和新的 term taxonomy IDs 合并。 |
array_merge() |
7. 清理 Term IDs | 对 term taxonomy IDs 进行清理,确保其中包含的是唯一的、新增的 IDs。 | array_unique() , array_diff() |
8. 插入新的 Relationships | 将新的 term taxonomy IDs 插入到 wp_term_relationships 表中,建立文章和 term taxonomy 之间的关联。 |
$wpdb->prepare() , $wpdb->query() |
9. 删除旧的 Relationships | 删除文章不再需要的 term relationships。 | array_diff() , $wpdb->prepare() , $wpdb->query() |
10. 清理缓存 | 清理与文章和 taxonomy 相关的缓存。 | wp_cache_delete() |
11. 触发 Actions | 触发一些 actions,允许其他插件或主题对文章的 term relationships 进行自定义操作。 | do_action() |
12. 更新计数 | 更新 taxonomy 的计数器。 | wp_update_term_count_now() |
13. 返回结果 | 返回新的 term taxonomy IDs 数组。 | array_map() , array_values() |
使用示例:给文章设置分类
$post_id = 123; // 文章ID
$categories = array( '新闻', '科技' ); // 要设置的分类名称
$taxonomy = 'category'; // 分类法名称
$result = wp_set_post_terms( $post_id, $categories, $taxonomy );
if ( is_wp_error( $result ) ) {
echo 'Error: ' . $result->get_error_message();
} else {
echo 'Categories set successfully!';
print_r( $result ); // 输出 Term Taxonomy IDs
}
这段代码演示了如何使用 wp_set_post_terms()
函数给 ID 为 123 的文章设置 "新闻" 和 "科技" 两个分类。
总结:wp_set_post_terms()
,WordPress 的分类大师
wp_set_post_terms()
函数是 WordPress 中一个非常重要的函数,它负责管理文章和分类术语之间的关系。理解它的源码,可以帮助咱们更好地理解 WordPress 的内部机制,也能让咱们在开发 WordPress 主题和插件时更加得心应手。
希望今天的讲解对大家有所帮助!下次有机会,咱们再聊聊 WordPress 的其他有趣的功能。谢谢大家!