各位老铁,大家好!今天咱们聊聊 WordPress 里一个低调但关键的函数:wp_update_term()
。这玩意儿就像分类目录和标签的幕后推手,负责更新它们的各种属性。咱们一起扒一扒它的源码,看看它是怎么玩转分类术语的,特别是别名(slug)和计数这两块。
一、wp_update_term()
:你的分类术语变形金刚
wp_update_term()
函数位于 wp-includes/taxonomy.php
文件中。它的作用,简单来说,就是修改已存在的分类术语。它不仅能改名字,还能改描述,最重要的是,它能帮你处理别名冲突,并更新分类术语的计数。
函数签名:
/**
* Updates a term.
*
* @since 3.0.0
*
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
* @param array|string $args {
* Optional. An array or string of arguments. Default empty array.
*
* @type string $name Term name. Default null.
* @type string $slug Term slug. Default null.
* @type int $parent Term parent ID. Default null.
* @type string $description Term description. Default null.
* }
* @return WP_Error|array {
* @type int $term_id The term ID.
* @type int $term_taxonomy_id The term taxonomy ID.
* }
*/
function wp_update_term( $term_id, $taxonomy, $args = array() ) {
// 函数体
}
参数说明:
$term_id
:要更新的术语的 ID。这是必须的,不然你让它更新谁?$taxonomy
:术语所属的分类法(taxonomy)。比如 ‘category’、’post_tag’ 等。这也是必须的。$args
:一个数组,包含要更新的字段。可以包括name
(术语名称)、slug
(别名)、parent
(父级术语 ID)、description
(描述)。
返回值:
- 成功时,返回一个数组,包含
$term_id
和$term_taxonomy_id
。 - 失败时,返回一个
WP_Error
对象,里面包含了错误信息。
二、源码解剖:一步一步来看
现在,咱们深入到 wp_update_term()
的源码中,看看它是怎么工作的。
function wp_update_term( $term_id, $taxonomy, $args = array() ) {
global $wpdb;
// 1. 参数验证
$term_id = (int) $term_id;
if ( empty( $term_id ) ) {
return new WP_Error( 'invalid_term_id', __( 'Empty term ID.' ) );
}
if ( empty( $taxonomy ) ) {
return new WP_Error( 'invalid_taxonomy', __( 'Empty taxonomy.' ) );
}
$term = get_term( $term_id, $taxonomy );
if ( is_wp_error( $term ) ) {
return $term;
}
if ( empty( $term ) ) {
return new WP_Error( 'invalid_term', __( 'Non-existent term.' ) );
}
// 2. 准备参数
$args = wp_parse_args( $args );
$name = null;
$slug = null;
$term_group = null;
$description = null;
$parent = null;
if ( isset( $args['name'] ) ) {
$name = trim( $args['name'] );
}
if ( isset( $args['slug'] ) ) {
$slug = trim( $args['slug'] );
}
if ( isset( $args['term_group'] ) ) {
$term_group = (int) $args['term_group'];
}
if ( isset( $args['description'] ) ) {
$description = trim( $args['description'] );
}
if ( isset( $args['parent'] ) ) {
$parent = (int) $args['parent'];
}
// 3. 数据清理和验证
if ( ! empty( $name ) ) {
$name = sanitize_term_field( 'name', $name, $term_id, $taxonomy, 'db' );
}
if ( isset( $slug ) ) { // Allow empty slug to be passed.
$slug = sanitize_term_field( 'slug', $slug, $term_id, $taxonomy, 'db' );
if ( empty( $slug ) && '0' !== $slug ) {
$slug = sanitize_title( $name );
}
}
if ( isset( $parent ) ) {
if ( $parent == $term_id ) {
return new WP_Error( 'invalid_parent', __( 'Cannot set parent to the current term.' ) );
}
// Check to prevent parent loop
$ancestors = get_ancestors( $term_id, $taxonomy, 'term_id' );
if ( is_wp_error( $ancestors ) ) {
return $ancestors;
}
if ( in_array( $parent, $ancestors, true ) ) {
return new WP_Error( 'invalid_parent', __( 'Cannot set parent to a child term.' ) );
}
}
// 4. 别名 (Slug) 冲突处理
if ( null !== $slug ) {
$original_slug = $slug;
$slug = wp_unique_term_slug( $slug, (object) array( 'taxonomy' => $taxonomy, 'term_id' => $term_id ) );
if ( is_wp_error( $slug ) ) {
return $slug;
}
}
// 5. 构建 SQL 查询
$fields = array();
$formats = array();
if ( null !== $name ) {
$fields['name'] = '%s';
$formats[] = $name;
}
if ( null !== $slug ) {
$fields['slug'] = '%s';
$formats[] = $slug;
}
if ( null !== $term_group ) {
$fields['term_group'] = '%d';
$formats[] = $term_group;
}
// 6. 执行更新
if ( ! empty( $fields ) ) {
$sql = "UPDATE $wpdb->terms SET";
$sets = array();
foreach ( $fields as $field => $format ) {
$sets[] = "`$field` = $format";
}
$sql .= ' ' . implode( ', ', $sets );
$sql .= " WHERE term_id = %d";
$formats[] = $term_id;
$sql = $wpdb->prepare( $sql, $formats );
$result = $wpdb->query( $sql );
if ( false === $result ) {
return new WP_Error( 'db_update_error', __( 'Could not update term in the database.' ), $wpdb->last_error );
}
}
// 7. 更新 term_taxonomy 表
$fields = array();
$formats = array();
if ( null !== $description ) {
$fields['description'] = '%s';
$formats[] = $description;
}
if ( null !== $parent ) {
$fields['parent'] = '%d';
$formats[] = $parent;
}
if ( ! empty( $fields ) ) {
$sql = "UPDATE $wpdb->term_taxonomy SET";
$sets = array();
foreach ( $fields as $field => $format ) {
$sets[] = "`$field` = $format";
}
$sql .= ' ' . implode( ', ', $sets );
$sql .= " WHERE term_id = %d AND taxonomy = %s";
$formats[] = $term_id;
$formats[] = $taxonomy;
$sql = $wpdb->prepare( $sql, $formats );
$result = $wpdb->query( $sql );
if ( false === $result ) {
return new WP_Error( 'db_update_error', __( 'Could not update term taxonomy in the database.' ), $wpdb->last_error );
}
}
// 8. 清理缓存
wp_cache_delete( $term_id, $taxonomy . '_term_id_cache' );
$tt_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id = %d AND taxonomy = %s", $term_id, $taxonomy ) );
foreach ( $tt_ids as $tt_id ) {
clean_term_cache( $tt_id, $taxonomy );
}
// 9. 更新计数
if ( null !== $parent ) {
_update_term_hierarchy( $taxonomy ); // This function deals with clearing the cache.
} else {
_update_generic_term_count( array( $term_id ), $taxonomy );
}
$term_taxonomy_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id = %d AND taxonomy = %s", $term_id, $taxonomy ) );
/**
* Fires after a term is updated.
*
* @since 4.4.0
*
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
*/
do_action( 'edit_term', $term_id, $taxonomy );
/**
* Fires after a term is updated.
*
* @since 2.3.0
* @deprecated 4.4.0 Use {@see 'edit_term'}.
*
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
*/
do_action( 'edited_term', $term_id, $taxonomy );
/**
* Fires after a specific term is updated.
*
* The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
*
* @since 3.0.0
*
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
*/
do_action( "edit_{$taxonomy}", $term_id );
/**
* Fires after a specific term is updated.
*
* The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
*
* @since 2.3.0
* @deprecated 4.4.0 Use {@see 'edit_term'}.
*
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
*/
do_action( "edited_{$taxonomy}", $term_id );
return array( 'term_id' => $term_id, 'term_taxonomy_id' => $term_taxonomy_ids[0] );
}
流程分解:
-
参数验证:
- 确保
$term_id
和$taxonomy
不为空,且$term_id
是一个整数。 - 使用
get_term()
函数获取术语对象,如果术语不存在,则返回错误。
- 确保
-
准备参数:
- 使用
wp_parse_args()
函数将$args
数组与默认参数合并。 - 提取
name
、slug
、term_group
、description
、parent
等参数的值。
- 使用
-
数据清理和验证:
- 使用
sanitize_term_field()
函数对name
和slug
进行清理,防止 XSS 攻击。 - 如果
slug
为空,则使用sanitize_title()
函数根据name
生成一个。 - 如果设置了
parent
,则检查是否会出现父子循环引用。
- 使用
-
别名(Slug)冲突处理:
- 这是整个函数的核心部分之一。使用
wp_unique_term_slug()
函数来确保别名是唯一的。 - 如果别名已经存在,
wp_unique_term_slug()
会自动在别名后面添加一个数字,直到找到一个唯一的别名。
- 这是整个函数的核心部分之一。使用
-
构建 SQL 查询:
- 根据要更新的字段,构建 SQL
UPDATE
查询语句。 - 使用
$wpdb->prepare()
函数对查询语句进行预处理,防止 SQL 注入。
- 根据要更新的字段,构建 SQL
-
执行更新:
- 使用
$wpdb->query()
函数执行 SQL 查询,更新wp_terms
表中的数据。
- 使用
-
更新
term_taxonomy
表:- 如果
$description
或$parent
发生了变化,则更新wp_term_taxonomy
表。
- 如果
-
清理缓存:
- 使用
wp_cache_delete()
函数删除与该术语相关的缓存,确保下次获取术语时,能得到最新的数据。 - 使用
clean_term_cache()
函数清理术语缓存。
- 使用
-
更新计数:
- 如果
$parent
发生了变化,则调用_update_term_hierarchy()
函数更新分类术语的层级关系。 - 否则,调用
_update_generic_term_count()
函数更新分类术语的计数。
- 如果
-
触发 Action Hook:
- 触发
edit_term
,edited_term
,edit_{$taxonomy}
,edited_{$taxonomy}
action hook,允许其他插件或主题对术语更新进行干预。
- 触发
-
返回结果:
- 返回一个数组,包含
$term_id
和$term_taxonomy_id
。
- 返回一个数组,包含
三、重点解析:别名(Slug)和计数
咱们重点看看 wp_update_term()
函数是如何处理别名冲突和更新计数的。
1. 别名(Slug)冲突处理:wp_unique_term_slug()
wp_unique_term_slug()
函数位于 wp-includes/taxonomy.php
文件中,它的作用是生成一个唯一的分类术语别名。
function wp_unique_term_slug( $slug, $term ) {
global $wpdb;
$taxonomy = $term->taxonomy;
$term_id = isset( $term->term_id ) ? $term->term_id : 0;
$original_slug = $slug;
$i = 2;
while ( true ) {
$found_slug = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.slug = %s AND t.term_id != %d LIMIT 1", $taxonomy, $slug, $term_id ) );
if ( ! $found_slug ) {
break;
}
$slug = $original_slug . "-$i";
$i++;
}
return $slug;
}
工作原理:
- 它会首先检查给定的别名
$slug
是否已经存在于指定的分类法$taxonomy
中。 - 如果存在,它会在别名后面添加一个数字,然后再次检查是否存在。
- 这个过程会一直重复,直到找到一个唯一的别名。
举个例子:
假设你有一个分类目录,名称为 "WordPress教程",别名也为 "wordpress教程"。现在你想把另一个分类目录的别名也设置为 "wordpress教程"。wp_unique_term_slug()
函数会帮你生成一个唯一的别名,比如 "wordpress教程-2"。
2. 更新计数:_update_generic_term_count()
_update_generic_term_count()
函数位于 wp-includes/taxonomy.php
文件中,它的作用是更新分类术语的计数,也就是有多少篇文章属于该分类术语。
function _update_generic_term_count( $terms, $taxonomy ) {
global $wpdb;
$object_types = (array) apply_filters( 'get_objects_for_term', array( 'post' ), $terms, $taxonomy );
foreach ( $object_types as $object_type ) {
$count = (array) $wpdb->get_results( $wpdb->prepare( "SELECT term_taxonomy_id, count(*) AS c FROM $wpdb->term_relationships WHERE term_taxonomy_id IN (SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id IN (%s) AND taxonomy = %s) AND object_id IN (SELECT ID FROM $wpdb->posts WHERE post_type = %s AND post_status IN ('publish', 'future', 'draft', 'pending', 'private')) GROUP BY term_taxonomy_id", implode( ',', array_map( 'intval', $terms ) ), $taxonomy, $object_type ), ARRAY_A );
foreach ( $count as $c ) {
$wpdb->update( $wpdb->term_taxonomy, array( 'count' => $c['c'] ), array( 'term_taxonomy_id' => $c['term_taxonomy_id'] ) );
wp_cache_delete( $c['term_taxonomy_id'], 'term_taxonomy' );
}
}
wp_cache_delete_multiple( $terms, $taxonomy . '_term_id_cache' );
}
工作原理:
- 它首先获取与该分类术语关联的对象类型(通常是 ‘post’)。
- 然后,它查询数据库,统计有多少篇文章属于该分类术语。
- 最后,它更新
wp_term_taxonomy
表中的count
字段,并清理缓存。
举个例子:
假设你有一个分类目录 "WordPress技巧",目前有 5 篇文章属于该分类目录。当你发布一篇新的文章,并将其添加到 "WordPress技巧" 分类目录时,_update_generic_term_count()
函数会将 "WordPress技巧" 分类目录的计数更新为 6。
四、实际应用:代码示例
咱们来看几个实际的例子,演示如何使用 wp_update_term()
函数。
示例 1:更新分类目录的名称和描述
$term_id = 123; // 要更新的分类目录的 ID
$taxonomy = 'category';
$args = array(
'name' => 'PHP高级技巧',
'description' => '这里分享一些关于 PHP 高级开发的技巧和经验。'
);
$result = wp_update_term( $term_id, $taxonomy, $args );
if ( is_wp_error( $result ) ) {
echo '更新失败:' . $result->get_error_message();
} else {
echo '更新成功!';
}
示例 2:更新标签的别名
$term_id = 456; // 要更新的标签的 ID
$taxonomy = 'post_tag';
$args = array(
'slug' => 'php-gaoji'
);
$result = wp_update_term( $term_id, $taxonomy, $args );
if ( is_wp_error( $result ) ) {
echo '更新失败:' . $result->get_error_message();
} else {
echo '更新成功!';
}
示例 3:更新分类目录的父级分类
$term_id = 789; // 要更新的分类目录的 ID
$taxonomy = 'category';
$args = array(
'parent' => 10 // 父级分类目录的 ID
);
$result = wp_update_term( $term_id, $taxonomy, $args );
if ( is_wp_error( $result ) ) {
echo '更新失败:' . $result->get_error_message();
} else {
echo '更新成功!';
}
五、注意事项:踩坑指南
- 权限问题: 确保当前用户有足够的权限来更新分类术语。
- 数据验证: 在调用
wp_update_term()
函数之前,一定要对数据进行验证,防止恶意数据。 - 缓存问题: 更新分类术语后,一定要清理缓存,确保下次获取术语时,能得到最新的数据。
- 别名冲突: 注意别名冲突问题,
wp_unique_term_slug()
函数会帮你处理,但是你最好还是提前规划好别名。 term_group
的使用:term_group
用于关联具有相似含义但属于不同分类法的术语。 用的不多。
六、总结:wp_update_term()
,分类术语的守护者
wp_update_term()
函数是 WordPress 中一个非常重要的函数,它负责更新分类术语的各种属性,包括名称、别名、描述、父级分类等。理解它的工作原理,可以帮助你更好地管理 WordPress 的分类目录和标签,并避免一些常见的错误。特别是要理解别名冲突处理和计数更新这两个核心功能。
希望今天的讲解对大家有所帮助! 下次再见!