各位老铁,晚上好!我是你们的老朋友,今天咱们来聊聊 WordPress 里面一个挺重要的函数:wp_update_term()
。这玩意儿,乍一听好像挺学术,但实际上,它就是负责更新分类术语信息的,比如你改个分类的名字,换个别名,都得靠它。而且,它还负责更新分类下的文章数量,也就是咱们常说的“计数更新”。
咱们今天就来扒一扒它的源码,看看它到底是怎么运作的,以及分类术语更新和计数更新的逻辑是什么。准备好了吗?Let’s go!
1. 初探 wp_update_term()
:这是个啥?
首先,咱们得搞清楚 wp_update_term()
这个函数是干嘛的。简单来说,它接受一些参数,然后去数据库里更新对应的分类术语信息。
它的基本用法是这样的:
$term_id = 123; // 要更新的分类术语 ID
$taxonomy = 'category'; // 分类法,比如 category(分类)、post_tag(标签)
$args = array(
'name' => '新的分类名称',
'slug' => 'new-category-slug',
'description' => '新的分类描述',
'parent' => 0 // 父级分类 ID,0 表示顶级分类
);
$result = wp_update_term( $term_id, $taxonomy, $args );
if ( is_wp_error( $result ) ) {
// 更新失败
echo '更新失败:' . $result->get_error_message();
} else {
// 更新成功
echo '更新成功!新的分类术语 ID 是:' . $result['term_id'];
}
看到了吧,wp_update_term()
接受三个参数:
$term_id
: 要更新的分类术语 ID。$taxonomy
: 分类法(taxonomy),比如 category(分类)、post_tag(标签)。$args
: 一个数组,包含了要更新的字段和对应的值。
2. 源码剖析:一步一步看它怎么跑
接下来,咱们来深入源码,看看 wp_update_term()
到底是怎么实现的。为了方便阅读,我这里只截取了核心代码,并加上了注释。
function wp_update_term( $term_id, $taxonomy, $args = array() ) {
global $wpdb;
$term_id = (int) $term_id;
if ( empty( $term_id ) || empty( $taxonomy ) ) {
return new WP_Error( 'missing_term_id', __( '缺少分类术语 ID 或分类法。' ) );
}
$term = get_term( $term_id, $taxonomy );
if ( is_wp_error( $term ) ) {
return $term;
}
if ( empty( $term ) ) {
return new WP_Error( 'invalid_term', __( '无效的分类术语。' ) );
}
$defaults = array(
'name' => $term->name,
'slug' => $term->slug,
'description' => $term->description,
'parent' => $term->parent,
);
$args = wp_parse_args( $args, $defaults );
$name = trim( sanitize_term_field( 'name', $args['name'], $term_id, $taxonomy, 'db' ) );
$slug = sanitize_title( $args['slug'] );
$description = trim( sanitize_term_field( 'description', $args['description'], $term_id, $taxonomy, 'db' ) );
$parent = (int) $args['parent'];
// 检查名称是否为空
if ( empty( $name ) ) {
return new WP_Error( 'empty_term_name', __( '分类术语名称不能为空。' ) );
}
// 检查别名是否重复
$slug_exists = term_exists( $slug, $taxonomy, $parent );
if ( $slug_exists && $slug_exists['term_id'] != $term_id ) {
return new WP_Error( 'term_exists', __( '该别名已存在。' ) );
}
$data = compact( 'name', 'slug', 'description', 'parent' );
$where = array( 'term_id' => $term_id );
/**
* Fires immediately before a term is updated in the database.
*
* @since 2.3.0
*
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
* @param array $args An array of term data.
*/
do_action( 'pre_update_term', $term_id, $taxonomy, $args );
$result = $wpdb->update( $wpdb->terms, $data, $where );
if ( false === $result ) {
return new WP_Error( 'db_update_error', __( '数据库更新失败。' ), $wpdb->last_error );
}
clean_term_cache( $term_id, $taxonomy, false );
/**
* Fires after a term is updated in the database.
*
* @since 2.3.0
*
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
* @param array $args An array of term data.
*/
do_action( 'edit_term', $term_id, $taxonomy, $args );
do_action( "edit_{$taxonomy}", $term_id, $taxonomy, $args );
do_action( 'edited_term', $term_id, $taxonomy, $args );
do_action( "edited_{$taxonomy}", $term_id, $taxonomy, $args );
wp_cache_delete( $term_id, $taxonomy . '_children' );
/**
* Fires after a term is updated in the database, but before the term object cache is cleaned.
*
* @since 4.4.0
*
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
*/
do_action( 'saved_term', $term_id, $taxonomy );
// 更新计数
_update_term_count( $term_id, $taxonomy );
/**
* Fires after a term has been updated in the database and the term object cache has been cleaned.
*
* @since 2.3.0
*
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
*/
do_action( 'update_term', $term_id, $taxonomy );
return array( 'term_id' => $term_id, 'term_taxonomy_id' => $term->term_taxonomy_id );
}
咱们来一步一步分析:
- 参数校验:
- 首先,函数会检查
$term_id
和$taxonomy
是否为空,如果为空,就返回一个错误。 - 然后,它会通过
get_term()
函数获取分类术语的信息,如果获取失败,也返回一个错误。
- 首先,函数会检查
- 参数合并:
wp_parse_args()
函数会将传入的$args
数组和默认值合并。这样,如果你只传入了要修改的字段,其他字段就会使用原来的值。
- 数据清洗:
sanitize_term_field()
函数会对$name
和$description
进行安全过滤,防止 XSS 攻击。sanitize_title()
函数会对$slug
进行处理,生成一个合法的别名。
- 数据验证:
- 函数会检查
$name
是否为空,如果为空,就返回一个错误。 term_exists()
函数会检查$slug
是否重复,如果重复,也返回一个错误。注意,这里会排除掉当前要更新的分类术语,也就是说,如果只是修改了其他字段,而$slug
没有改变,就不会报错。
- 函数会检查
- 数据库更新:
$wpdb->update()
函数会将数据更新到数据库中。
- 缓存清理:
clean_term_cache()
函数会清理分类术语的缓存,确保下次访问时能获取到最新的数据。
- 钩子(Actions):
- 在更新前后,会触发一系列的钩子,允许开发者在这些关键点插入自定义的代码。
- 计数更新:
_update_term_count()
函数会更新分类术语下的文章数量。这个函数是咱们今天重点要讲的。
- 返回结果:
- 函数会返回一个数组,包含了分类术语 ID 和分类法 ID。
3. 计数更新:_update_term_count()
的秘密
咱们再来看看 _update_term_count()
函数,它负责更新分类术语下的文章数量。这个函数稍微复杂一些,因为它需要考虑不同的文章状态(比如发布、草稿、私有等)和不同的分类法。
function _update_term_count( $terms, $taxonomy, $do_deferred = true ) {
global $wpdb;
$object_types = (array) get_object_taxonomies( $taxonomy, 'names' );
if ( empty( $object_types ) ) {
return;
}
$terms = (array) $terms;
$terms = array_map( 'intval', $terms );
$object_types = array_map( array( $wpdb, 'escape' ), $object_types );
$terms = array_map( array( $wpdb, 'escape' ), $terms );
$taxonomy = esc_sql( $taxonomy );
foreach ( $object_types as $object_type ) {
$count = $wpdb->get_results(
"
SELECT term_taxonomy_id, COUNT(*) AS count FROM {$wpdb->term_relationships} AS tr
INNER JOIN {$wpdb->posts} AS p ON (p.ID = tr.object_id)
WHERE tr.term_taxonomy_id IN (SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE term_id IN (" . implode( ',', $terms ) . ") AND taxonomy = '$taxonomy')
AND p.post_type = '$object_type' AND p.post_status IN ('publish', 'future', 'private')
GROUP BY tr.term_taxonomy_id
"
, ARRAY_A );
foreach ( $count as $c ) {
$wpdb->update( $wpdb->term_taxonomy, array( 'count' => $c['count'] ), array( 'term_taxonomy_id' => $c['term_taxonomy_id'] ) );
wp_cache_delete( $c['term_taxonomy_id'], 'term_taxonomy' );
}
}
wp_cache_delete( 'get_terms', $taxonomy );
if ( $do_deferred ) {
wp_defer_term_counting( false );
}
}
咱们来解读一下:
- 获取对象类型:
get_object_taxonomies()
函数会获取与当前分类法关联的对象类型(object types),比如 post、page 等。
- 数据清洗:
- 函数会对
$terms
、$object_types
和$taxonomy
进行安全过滤,防止 SQL 注入。
- 函数会对
- 循环对象类型:
- 函数会循环遍历每个对象类型,然后执行 SQL 查询。
- SQL 查询:
- 这个 SQL 查询是核心,它会统计每个分类术语下,指定对象类型(比如 post)的文章数量,并且只统计
publish
、future
和private
状态的文章。 - 查询语句比较复杂,简单来说,它做了以下几件事:
- 连接
wp_term_relationships
表和wp_posts
表,找到所有与指定分类术语关联的文章。 - 根据文章类型和文章状态进行过滤。
- 按照
term_taxonomy_id
进行分组,统计每个分类术语下的文章数量。
- 连接
- 这个 SQL 查询是核心,它会统计每个分类术语下,指定对象类型(比如 post)的文章数量,并且只统计
- 更新计数:
$wpdb->update()
函数会将统计结果更新到wp_term_taxonomy
表中。
- 缓存清理:
wp_cache_delete()
函数会清理分类术语的缓存。
- 延迟计数:
wp_defer_term_counting()
函数用于延迟计数,避免在高并发情况下频繁更新计数,影响性能。
4. 分类术语更新与计数更新的逻辑
现在,咱们来总结一下分类术语更新和计数更新的逻辑:
步骤 | 分类术语更新 | 计数更新 |
---|---|---|
1 | 接收参数:term_id , taxonomy , args |
接收参数:term_id , taxonomy |
2 | 参数校验:检查 term_id 和 taxonomy 是否为空,以及分类术语是否存在 |
获取对象类型:获取与分类法关联的对象类型(比如 post) |
3 | 参数合并:将传入的 args 数组和默认值合并 |
数据清洗:对参数进行安全过滤 |
4 | 数据清洗:对 name 和 description 进行安全过滤,对 slug 进行处理 |
循环对象类型:遍历每个对象类型 |
5 | 数据验证:检查 name 是否为空,检查 slug 是否重复 |
SQL 查询:统计每个分类术语下,指定对象类型的文章数量 |
6 | 数据库更新:将数据更新到 wp_terms 表中 |
更新计数:将统计结果更新到 wp_term_taxonomy 表中 |
7 | 缓存清理:清理分类术语的缓存 | 缓存清理:清理分类术语的缓存 |
8 | 触发钩子:在更新前后触发一系列的钩子 | 延迟计数:根据情况延迟计数 |
9 | 返回结果:返回分类术语 ID 和分类法 ID |
可以看到,分类术语更新主要负责更新分类术语的基本信息,比如名称、别名、描述等。而计数更新则负责更新分类术语下的文章数量。
5. 注意事项
- 性能问题: 频繁更新分类术语可能会影响性能,特别是当文章数量很多时。因此,应该尽量避免频繁更新。
- 缓存问题: 在更新分类术语后,一定要清理缓存,确保下次访问时能获取到最新的数据。
- 数据安全: 在处理用户输入的数据时,一定要进行安全过滤,防止 XSS 攻击和 SQL 注入。
- 钩子: 可以利用 WordPress 提供的钩子,在更新前后插入自定义的代码,实现更灵活的功能。
6. 总结
今天,咱们深入剖析了 WordPress 的 wp_update_term()
函数,了解了它的实现原理,以及分类术语更新和计数更新的逻辑。希望通过今天的讲解,大家能够对 WordPress 的分类术语管理有更深入的理解。
好了,今天的讲座就到这里,谢谢大家!如果有什么问题,欢迎在评论区留言,咱们一起探讨。下次再见!