分析 `wp_insert_term()` 和 `wp_delete_term()` 函数的源码,它们如何处理分类术语的数据库操作和缓存清理?

各位观众,欢迎来到今天的 WordPress 源码探秘讲座!今天我们要聊的是 WordPress 分类术语的“生老病死”——也就是 wp_insert_term()wp_delete_term() 这两个函数。

咱们先轻松一下,想象一下:你在 WordPress 站点上创建了一个新的分类“编程语言”,或者决定清理掉一个不再使用的标签“冷门技术”。这些动作背后,都是这两个函数在默默工作。

那么,它们到底是如何操作数据库,又是如何清理缓存,以保证我们的站点运行顺畅的呢?别急,咱们这就一层层剥开它们神秘的面纱!

一、wp_insert_term():新生命的诞生

wp_insert_term() 函数的作用是插入一个新的分类术语到数据库中。让我们深入源码,看看它是如何实现的。

function wp_insert_term( $term, $taxonomy, $args = array() ) {
    global $wpdb;

    // 1. 参数校验和准备
    $term = trim( $term );
    $taxonomy = trim( $taxonomy );

    if ( empty( $term ) || empty( $taxonomy ) ) {
        return new WP_Error( 'empty_term_name', __( 'A name is required for this term.' ) );
    }

    if ( strlen( $term ) > 200 ) {
        return new WP_Error( 'term_name_too_long', __( 'Term names must be less than 200 characters.' ) );
    }

    if ( term_exists( $term, $taxonomy ) ) {
        return new WP_Error( 'term_exists', __( 'Term name already exists.' ) );
    }

    $defaults = array(
        'description' => '',
        'slug'        => '',
        'parent'      => 0,
    );

    $args = wp_parse_args( $args, $defaults );

    $description = trim( $args['description'] );
    $slug        = trim( $args['slug'] );
    $parent      = (int) $args['parent'];

    // 2. Slug 处理
    if ( empty( $slug ) ) {
        $slug = sanitize_title( $term );
    }

    $slug = wp_unique_term_slug( $slug, (object) array(
        'taxonomy' => $taxonomy,
        'parent'   => $parent,
    ) );

    // 3. 数据库插入
    $data = compact( 'name', 'slug' );
    $format = array( '%s', '%s' );

    $wpdb->insert( $wpdb->terms, $data, $format );
    $term_id = (int) $wpdb->insert_id;

    // 4. taxonomy 关系插入
    $data = compact( 'term_id', 'taxonomy', 'description', 'parent' );
    $format = array( '%d', '%s', '%s', '%d' );

    $wpdb->insert( $wpdb->term_taxonomy, $data, $format );
    $tt_id = (int) $wpdb->insert_id;

    // 5. 清理缓存
    clean_term_cache( $term_id, $taxonomy );

    // 6. 返回结果
    return array(
        'term_id' => $term_id,
        'term_taxonomy_id' => $tt_id,
    );
}

咱们把这个函数分解成几个关键步骤:

  1. 参数校验和准备

    • 首先,函数会检查传入的术语名称和分类法是否为空,长度是否超过限制。
    • 接着,它会检查该术语是否已经存在。如果存在,就返回一个错误。
    • 然后,它会使用 wp_parse_args() 函数将传入的 $args 参数与默认值合并,处理描述、slug 和父级分类。
  2. Slug 处理

    • 如果用户没有指定 slug,函数会自动根据术语名称生成一个。
    • wp_unique_term_slug() 函数会确保生成的 slug 在当前分类法下是唯一的,避免冲突。
  3. 数据库插入 (wp_terms)

    • 使用 $wpdb->insert() 函数将术语名称和 slug 插入到 wp_terms 表中。
    • $wpdb->insert_id 记录了插入的ID。
    • wp_terms 表存储了所有术语的基本信息,包括名称和 slug。
    -- 示例:wp_terms 表结构
    CREATE TABLE `wp_terms` (
      `term_id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
      `name` varchar(200) NOT NULL DEFAULT '',
      `slug` varchar(200) NOT NULL DEFAULT '',
      `term_group` bigint(10) NOT NULL DEFAULT '0',
      PRIMARY KEY (`term_id`),
      KEY `slug` (`slug`(191)),
      KEY `name` (`name`(191))
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
  4. taxonomy 关系插入 (wp_term_taxonomy)

    • 使用 $wpdb->insert() 函数将术语 ID、分类法、描述和父级分类 ID 插入到 wp_term_taxonomy 表中。
    • wp_term_taxonomy 表存储了术语与分类法之间的关系,以及一些额外的属性,比如描述和父级分类。
    -- 示例:wp_term_taxonomy 表结构
    CREATE TABLE `wp_term_taxonomy` (
      `term_taxonomy_id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
      `term_id` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
      `taxonomy` varchar(32) NOT NULL DEFAULT '',
      `description` longtext NOT NULL,
      `parent` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
      `count` bigint(20) NOT NULL DEFAULT '0',
      PRIMARY KEY (`term_taxonomy_id`),
      UNIQUE KEY `term_id_taxonomy` (`term_id`,`taxonomy`),
      KEY `taxonomy` (`taxonomy`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
  5. 清理缓存

    • 调用 clean_term_cache() 函数清理与该术语相关的缓存。这是非常关键的一步,确保站点显示的是最新的数据。
  6. 返回结果

    • 函数返回一个包含 term_idterm_taxonomy_id 的数组。

关于 clean_term_cache() 函数

clean_term_cache() 函数负责清理与特定术语相关的缓存。它主要清理以下缓存:

  • 术语对象缓存:通过 clean_object_term_cache() 函数清理。
  • 分类法缓存:清理与该分类法相关的缓存,确保分类法信息是最新的。
  • 术语关系缓存:清理术语关系缓存,确保术语之间的关系正确。

总结:wp_insert_term() 的工作流程

步骤 描述 涉及函数/表
1 参数校验和准备:检查术语名称、分类法是否为空,长度是否超过限制,以及术语是否已经存在。 term_exists(), wp_parse_args()
2 Slug 处理:生成唯一的 slug,避免冲突。 sanitize_title(), wp_unique_term_slug()
3 数据库插入 (wp_terms):将术语名称和 slug 插入到 wp_terms 表中。 $wpdb->insert(), wp_terms
4 taxonomy 关系插入 (wp_term_taxonomy):将术语 ID、分类法、描述和父级分类 ID 插入到 wp_term_taxonomy 表中。 $wpdb->insert(), wp_term_taxonomy
5 清理缓存:清理与该术语相关的缓存,确保站点显示的是最新的数据。 clean_term_cache()
6 返回结果:返回一个包含 term_idterm_taxonomy_id 的数组。

二、wp_delete_term():谢幕与告别

wp_delete_term() 函数的作用是从数据库中删除一个分类术语。让我们看看它是如何“优雅地”删除数据的。

function wp_delete_term( $term, $taxonomy, $args = array() ) {
    global $wpdb;

    // 1. 参数校验
    $term = (int) $term;
    if ( empty( $term ) ) {
        return new WP_Error( 'invalid_term', __( 'Invalid term ID.' ) );
    }

    $taxonomy = trim( $taxonomy );
    if ( empty( $taxonomy ) ) {
        return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) );
    }

    // 2. 检查术语是否存在
    $term_exists = term_exists( $term, $taxonomy );
    if ( is_null( $term_exists ) ) {
        return new WP_Error( 'invalid_term', __( 'Nonexistent term ID.' ) );
    }

    // 3. 处理默认参数
    $defaults = array( 'force_default' => false );
    $args = wp_parse_args( $args, $defaults );

    $object_ids = array();

    // 4. 删除前钩子
    do_action( 'pre_delete_term', $term, $taxonomy );

    // 5. 检查是否设置为默认分类,如果是,则拒绝删除(除非强制删除)
    if ( 'category' === $taxonomy && get_option( 'default_category' ) == $term && ! $args['force_default'] ) {
        return new WP_Error( 'default_category', __( 'You cannot delete the default category.' ) );
    }

    // 6. 删除术语关系
    $tt_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE term_id = %d AND taxonomy = %s", $term, $taxonomy ) );

    foreach ( $tt_ids as $tt_id ) {
        $object_ids = array_merge( $object_ids, (array) $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) ) );
        wp_remove_object_terms( $object_ids, $term, $taxonomy ); //移除对象关联
        do_action( 'delete_term_taxonomy', $tt_id, $taxonomy ); //删除term_taxonomy前钩子
        $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) ); //删除term_taxonomy数据
        do_action( 'deleted_term_taxonomy', $tt_id, $taxonomy ); //删除term_taxonomy后钩子
    }

    // 7. 删除术语本身
    $result = $wpdb->delete( $wpdb->terms, array( 'term_id' => $term ) );

    // 8. 清理缓存
    clean_term_cache( $term );

    // 9. 删除后钩子
    do_action( 'delete_term', $term, $taxonomy );

    // 10. 返回结果
    return $result;
}

我们来分解一下这个函数的执行流程:

  1. 参数校验

    • 函数首先会将传入的术语 ID 转换为整数,并检查术语 ID 和分类法是否为空。
  2. 检查术语是否存在

    • 使用 term_exists() 函数检查要删除的术语是否存在。如果不存在,则返回一个错误。
  3. 处理默认参数

    • 使用 wp_parse_args() 函数处理默认参数,主要是 force_default,用于强制删除默认分类。
  4. 删除前钩子

    • do_action( 'pre_delete_term', $term, $taxonomy ),在删除术语之前执行的钩子,允许插件或主题执行自定义操作。
  5. 检查是否设置为默认分类

    • 如果尝试删除的是默认分类,并且没有设置 force_defaulttrue,则拒绝删除。
  6. 删除术语关系

    • 首先,从 wp_term_taxonomy 表中获取与该术语和分类法相关的所有 term_taxonomy_id
    • 然后,遍历这些 term_taxonomy_id,执行以下操作:
      • wp_term_relationships 表中获取与该 term_taxonomy_id 相关的所有对象 ID。
      • 使用 wp_remove_object_terms() 函数移除对象与该术语的关联。
      • do_action( 'delete_term_taxonomy', $tt_id, $taxonomy ) 在删除 term_taxonomy 记录前执行的钩子。
      • 使用 $wpdb->delete() 函数从 wp_term_taxonomy 表中删除对应的记录。
      • do_action( 'deleted_term_taxonomy', $tt_id, $taxonomy ) 在删除 term_taxonomy 记录后执行的钩子。
  7. 删除术语本身

    • 使用 $wpdb->delete() 函数从 wp_terms 表中删除对应的术语。
  8. 清理缓存

    • 调用 clean_term_cache() 函数清理与该术语相关的缓存。
  9. 删除后钩子

    • do_action( 'delete_term', $term, $taxonomy ),在删除术语之后执行的钩子,允许插件或主题执行自定义操作。
  10. 返回结果

    • 返回 $wpdb->delete() 函数的执行结果,表示是否成功删除术语。

关于 wp_remove_object_terms() 函数

wp_remove_object_terms() 函数用于移除对象与术语之间的关联。它从 wp_term_relationships 表中删除相关的记录。

总结:wp_delete_term() 的工作流程

步骤 描述 涉及函数/表
1 参数校验:检查术语 ID 和分类法是否为空。
2 检查术语是否存在:使用 term_exists() 函数检查要删除的术语是否存在。 term_exists()
3 处理默认参数:使用 wp_parse_args() 函数处理默认参数,主要是 force_default,用于强制删除默认分类。 wp_parse_args()
4 删除前钩子:do_action( 'pre_delete_term', $term, $taxonomy ),在删除术语之前执行的钩子。
5 检查是否设置为默认分类:如果尝试删除的是默认分类,并且没有设置 force_defaulttrue,则拒绝删除。 get_option()
6 删除术语关系:从 wp_term_taxonomy 表中获取与该术语和分类法相关的所有 term_taxonomy_id,并遍历这些 ID,移除对象与该术语的关联,然后从 wp_term_taxonomy 表中删除对应的记录。 $wpdb->get_col(), wp_remove_object_terms(), $wpdb->delete(), wp_term_relationships, wp_term_taxonomy
7 删除术语本身:使用 $wpdb->delete() 函数从 wp_terms 表中删除对应的术语。 $wpdb->delete(), wp_terms
8 清理缓存:调用 clean_term_cache() 函数清理与该术语相关的缓存。 clean_term_cache()
9 删除后钩子:do_action( 'delete_term', $term, $taxonomy ),在删除术语之后执行的钩子。
10 返回结果:返回 $wpdb->delete() 函数的执行结果,表示是否成功删除术语。

三、缓存的重要性

wp_insert_term()wp_delete_term() 函数中,clean_term_cache() 函数都扮演着至关重要的角色。为什么缓存清理如此重要呢?

简单来说,缓存是为了提高网站的性能。WordPress 会将一些常用的数据(比如分类术语信息)存储在缓存中,这样下次访问时就不用再查询数据库,从而加快页面加载速度。

但是,如果数据库中的数据发生了变化(比如我们添加或删除了一个分类术语),而缓存没有及时更新,就会导致网站显示的是旧的数据,造成信息不一致。

因此,clean_term_cache() 函数的作用就是确保缓存与数据库中的数据保持同步,避免出现数据不一致的问题。

四、实战演练

为了更好地理解这两个函数的使用,我们来做几个简单的演示:

1. 创建一个新的分类术语

$result = wp_insert_term(
    'WordPress 开发',
    'category',
    array(
        'description' => '关于 WordPress 开发的文章',
        'slug'        => 'wordpress-development',
        'parent'      => 0,
    )
);

if ( is_wp_error( $result ) ) {
    echo 'Error: ' . $result->get_error_message();
} else {
    echo '分类术语创建成功!Term ID: ' . $result['term_id'] . ', Term Taxonomy ID: ' . $result['term_taxonomy_id'];
}

2. 删除一个分类术语

$term_id = 5; // 替换为你要删除的术语 ID
$taxonomy = 'category';

$result = wp_delete_term( $term_id, $taxonomy );

if ( is_wp_error( $result ) ) {
    echo 'Error: ' . $result->get_error_message();
} else {
    echo '分类术语删除成功!';
}

五、总结

今天,我们一起深入探讨了 wp_insert_term()wp_delete_term() 这两个 WordPress 函数的源码,了解了它们是如何操作数据库、处理 slug、以及清理缓存的。

希望通过今天的讲座,大家能够对 WordPress 分类术语的管理机制有更深入的理解,并在实际开发中更加得心应手。

下次有机会,我们再一起探索 WordPress 源码的其他奥秘! 谢谢大家!

发表回复

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