详解 WordPress `wp_set_post_terms()` 函数源码:文章分类术语设置与 `taxonomy` 计数。

各位观众,晚上好!我是你们的老朋友,今天咱们来聊聊WordPress里一个特别重要的函数:wp_set_post_terms()。它掌管着文章的分类,标签等术语的设置,直接影响着你的网站内容是如何组织和展示的。听起来有点严肃,但别担心,咱们用大白话把它掰开了揉碎了讲清楚,保证你听完之后能像操作乐高积木一样玩转它。

开场白:术语的重要性,WordPress分类的基石

想象一下,你开了一家书店,如果不把书籍按照小说、散文、历史等分类摆放,顾客进店后是不是会一脸懵?WordPress也是一样,如果没有分类术语,所有的文章都堆在一起,那简直就是一场灾难。

wp_set_post_terms() 就是WordPress用来给文章贴标签,做分类的关键函数。它允许你设置文章的分类、标签等“术语”,让你的网站内容井井有条。

第一幕:wp_set_post_terms() 函数的基本结构

咱们先来看看 wp_set_post_terms() 的基本结构,知己知彼,百战不殆嘛。

/**
 * Sets the terms for a post.
 *
 * @since 2.3.0
 *
 * @param int          $post_id Post ID.
 * @param int|string|array $terms    Terms. Can be single term ID, single term name, array of term IDs, or array of term names.
 *                                   Names will be sanitized and matched against existing terms.
 * @param string       $taxonomy Taxonomy.
 * @param bool         $append Optional. Append terms to the existing terms. Default false (replace).
 * @return array|WP_Error Array of term taxonomy IDs, WP_Error on failure.
 */
function wp_set_post_terms( $post_id, $terms, $taxonomy, $append = false ) {
  // 函数体将在后面详细讲解
}

简单解释一下这几个参数:

  • $post_id:文章的ID,告诉WordPress你要修改哪篇文章的术语。
  • $terms:术语。可以是一个术语的ID,也可以是术语的名字,还可以是一个包含多个ID或名字的数组。
  • $taxonomy:分类法。例如 ‘category’(分类目录)、’post_tag’(标签)等等,告诉WordPress你要修改哪种类型的术语。
  • $append:是否追加。true 表示在现有术语的基础上添加,false 表示替换现有的术语。

第二幕:源码剖析,步步为营

现在,让我们深入 wp_set_post_terms() 的源码,看看它到底是怎么工作的。

  1. 参数校验与类型转换

    首先,函数会对传入的参数进行校验,确保数据的有效性。同时,还会将 $terms 转换成统一的数组格式,方便后续处理。

    $post_id = (int) $post_id;
    
    $taxonomy = sanitize_key( $taxonomy );
    
    if ( ! taxonomy_exists( $taxonomy ) ) {
     return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) );
    }
    
    if ( ! is_array( $terms ) ) {
     if ( ! empty( $terms ) ) {
       $terms = array( $terms );
     } else {
       $terms = array();
     }
    }

    这里用到了 sanitize_key() 函数,它的作用是对 taxonomy 进行安全过滤,防止注入攻击。taxonomy_exists() 函数则用于判断指定的分类法是否存在,如果不存在,则返回一个 WP_Error 对象。

  2. 术语ID获取

    接下来,函数会尝试将 $terms 数组中的术语名称转换为术语ID。

    $term_ids = array();
    foreach ( $terms as $term ) {
     if ( is_numeric( $term ) ) {
       $term_ids[] = (int) $term;
     } else {
       $t = term_exists( $term, $taxonomy );
       if ( $t ) {
         $term_ids[] = (int) $t['term_id'];
       } else {
         // 创建新的术语。实际代码中会更复杂,这里简化一下
         $term_info = wp_insert_term( $term, $taxonomy );
         if ( ! is_wp_error( $term_info ) ) {
           $term_ids[] = (int) $term_info['term_id'];
         } else {
           return $term_info; // 返回错误信息
         }
       }
     }
    }

    这段代码循环遍历 $terms 数组,如果术语是数字,则直接将其转换为整数,并添加到 $term_ids 数组中。如果术语是字符串,则使用 term_exists() 函数判断该术语是否存在。如果存在,则获取其ID并添加到 $term_ids 数组中;如果不存在,则使用 wp_insert_term() 函数创建一个新的术语,并获取其ID。

    注意: wp_insert_term() 是一个比较复杂的函数,它涉及到术语的名称、别名、描述等信息的处理。

  3. 术语关系设置

    现在,我们已经得到了一个包含所有术语ID的 $term_ids 数组。接下来,函数会根据 $append 参数的值,决定是追加术语还是替换术语。

    if ( $append ) {
     $current_term_ids = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'ids' ) );
     if ( ! is_wp_error( $current_term_ids ) ) {
       $term_ids = array_merge( $current_term_ids, $term_ids );
     }
    }
    
    $term_ids = array_unique( array_map( 'intval', $term_ids ) );

    如果 $appendtrue,则先使用 wp_get_object_terms() 函数获取当前文章已有的术语ID,然后将新的术语ID与已有的术语ID合并。array_unique() 函数用于去除重复的术语ID,array_map( 'intval', $term_ids ) 确保所有ID都是整数类型。

  4. 更新术语关系

    最后,函数使用 wp_set_object_terms() 函数来更新文章与术语之间的关系。

    $tt_ids = wp_set_object_terms( $post_id, $term_ids, $taxonomy, $append );
    
    if ( is_wp_error( $tt_ids ) ) {
     return $tt_ids;
    }

    wp_set_object_terms() 函数是真正执行术语关系设置的函数。它会将文章与指定的术语关联起来,并更新数据库中的相关数据。

  5. 计数器更新
    wp_set_object_terms() 会自动更新术语的计数。 每个术语都有一个计数器,记录有多少文章使用了该术语。当文章添加或删除术语时,这个计数器需要更新。

第三幕:taxonomy计数器的奥秘

wp_set_post_terms() 背后的一个重要机制是 taxonomy 计数器。每个 taxonomy 下的每个 term 都有一个计数器,用来记录有多少文章使用了该 term。 这个计数器对于显示分类目录中的文章数量至关重要。

  • 计数器的作用

    计数器主要用于以下几个方面:

    • 展示分类目录的文章数量: 在分类目录页面,我们会看到每个分类目录后面都有一个数字,表示该分类目录下的文章数量。
    • 判断分类目录是否为空: 如果一个分类目录的计数器为 0,则表示该分类目录下没有文章。
    • 优化查询性能: 通过计数器,我们可以快速获取分类目录下的文章数量,而无需查询数据库。
  • 计数器的更新

    计数器通常在以下情况下更新:

    • 文章被添加到分类目录: 当一篇文章被添加到某个分类目录时,该分类目录的计数器会增加 1。
    • 文章从分类目录中移除: 当一篇文章从某个分类目录中移除时,该分类目录的计数器会减少 1。
    • 分类目录被删除: 当一个分类目录被删除时,所有使用该分类目录的文章都会被更新,移除与该分类目录的关联。
  • 如何手动更新计数器

    WordPress提供了一个函数 wp_update_term_count() 用来手动更新计数器。

    /**
     * Updates the term count.
     *
     * @since 2.3.0
     *
     * @param int|array $terms Term ID or array of term IDs.
     * @param string $taxonomy Taxonomy name.
     */
    function wp_update_term_count( $terms, $taxonomy ) {
      global $wpdb;
    
      $terms = (array) $terms;
    
      foreach ( $terms as $term ) {
        $count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND term_taxonomy_id = %d", $term ) );
    
        do_action( 'edit_term_count', $term, $taxonomy );
    
        $wpdb->update( $wpdb->term_taxonomy, array( 'count' => $count ), array( 'term_taxonomy_id' => $term ) );
    
        do_action( 'edited_term_count', $term, $taxonomy );
      }
    
      wp_cache_delete( 'get_terms', $taxonomy );
    }

    这个函数接受两个参数:

    • $terms:要更新计数器的术语ID,可以是一个ID,也可以是一个包含多个ID的数组。
    • $taxonomy:分类法名称。

    这个函数会查询数据库,统计使用指定术语的文章数量,然后更新 wp_term_taxonomy 表中的 count 字段。

    何时使用 wp_update_term_count()

    通常情况下,WordPress会自动更新计数器,你不需要手动调用 wp_update_term_count()。 但是,在某些特殊情况下,例如你直接修改了数据库,或者使用了某些插件导致计数器不准确,你可能需要手动更新计数器。

    示例:

    // 更新分类ID为 5 的分类目录的计数器
    wp_update_term_count( 5, 'category' );
    
    // 更新标签ID为 10 和 12 的标签的计数器
    wp_update_term_count( array( 10, 12 ), 'post_tag' );

第四幕:实战演练,代码说话

说了这么多理论,不如来点实际的。下面我们来看几个使用 wp_set_post_terms() 的例子。

  • 示例1:设置文章的分类目录

    $post_id = 123; // 文章ID
    $categories = array( 1, 2, 3 ); // 分类目录ID,假设 1, 2, 3 分别代表 '新闻', '技术', '生活'
    $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 updated successfully!';
    }

    这段代码将文章ID为 123 的文章的分类目录设置为 ‘新闻’,’技术’ 和 ‘生活’。

  • 示例2:添加文章的标签

    $post_id = 456; // 文章ID
    $tags = array( 'WordPress', 'PHP', '前端' ); // 标签名称
    $taxonomy = 'post_tag';
    $append = true; // 追加标签
    
    $result = wp_set_post_terms( $post_id, $tags, $taxonomy, $append );
    
    if ( is_wp_error( $result ) ) {
      echo 'Error: ' . $result->get_error_message();
    } else {
      echo 'Tags updated successfully!';
    }

    这段代码将文章ID为 456 的文章添加了 ‘WordPress’,’PHP’ 和 ‘前端’ 三个标签。注意,这里使用了 $append = true,表示在原有标签的基础上添加新的标签。

  • 示例3:替换文章的标签

    $post_id = 789; // 文章ID
    $tags = array( 'JavaScript', 'CSS' ); // 标签名称
    $taxonomy = 'post_tag';
    $append = false; // 替换标签
    
    $result = wp_set_post_terms( $post_id, $tags, $taxonomy, $append );
    
    if ( is_wp_error( $result ) ) {
      echo 'Error: ' . $result->get_error_message();
    } else {
      echo 'Tags updated successfully!';
    }

    这段代码将文章ID为 789 的文章的标签替换为 ‘JavaScript’ 和 ‘CSS’。由于 $append = false,所以文章原有的标签会被删除。

第五幕:注意事项与最佳实践

  • 性能优化

    频繁调用 wp_set_post_terms() 可能会影响网站的性能。 尽量避免在循环中调用它。 如果需要批量更新文章的术语,可以考虑使用 wp_set_object_terms() 函数,它可以一次性更新多个文章的术语。

  • 错误处理

    在使用 wp_set_post_terms() 时,一定要注意错误处理。 函数可能会返回一个 WP_Error 对象,你需要检查返回值,并根据错误信息进行相应的处理。

  • 数据安全

    务必对传入的参数进行安全过滤,防止注入攻击。 特别是 $terms 参数,如果直接从用户输入获取,一定要使用 sanitize_text_field() 函数进行过滤。

  • 合理使用 $append 参数

    $append 参数决定是追加术语还是替换术语,要根据实际需求选择合适的值。 如果不确定,最好先备份数据,以免造成数据丢失。

第六幕:常见问题解答

  • Q:为什么我的分类目录计数器不准确?

    A:可能是因为缓存问题,或者某些插件导致计数器更新失败。 可以尝试清除缓存,或者手动调用 wp_update_term_count() 函数来更新计数器。

  • Q:如何获取文章的分类目录?

    A:可以使用 wp_get_post_terms() 函数来获取文章的分类目录。

  • Q:如何创建新的分类目录?

    A:可以使用 wp_insert_term() 函数来创建新的分类目录。

总结:wp_set_post_terms() 的威力

wp_set_post_terms() 是一个功能强大的函数,它可以让你轻松地管理文章的分类、标签等术语。 掌握了它,你就掌握了WordPress内容组织的关键,可以更好地组织和展示你的网站内容。希望今天的讲解能帮助你更好地理解和使用这个函数。记住,实践是检验真理的唯一标准,多动手尝试,你就能真正掌握它!

好了,今天的讲座就到这里,感谢大家的观看! 如果有什么问题,欢迎在评论区留言,我会尽力解答。我们下期再见!

发表回复

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