剖析 `get_term_by()` 函数的源码,解释它如何根据 “ 参数动态构建查询条件并获取分类术语。

各位观众老爷们,晚上好!今天咱们来聊聊 WordPress 里的一个“寻宝神器”—— get_term_by() 函数。别看它名字平平无奇,但它能根据你提供的各种线索,在分类法(Taxonomy)的茫茫大海中,精确地找到你想要的分类术语(Term)。

咱们要深入剖析一下它的源码,看看它到底是怎么做到“指哪打哪”的。准备好了吗?咱们这就开始!

1. get_term_by() 函数的真面目

首先,让我们来认识一下 get_term_by() 函数的基本结构。在 WordPress 的 wp-includes/taxonomy.php 文件中,你能找到它的身影。它的函数原型如下:

function get_term_by( string $field, string|int $value, string|array $taxonomy = 'category', string $output = OBJECT, string $filter = 'raw' ): WP_Term|false|null {
    // ...函数体...
}
  • $field:指定要搜索的字段。比如 ‘id’,’slug’,’name’ 等等。
  • $value:要搜索的字段对应的值。
  • $taxonomy:可选参数,指定分类法。默认为 ‘category’。可以是一个字符串,也可以是一个包含多个分类法名称的数组。
  • $output:可选参数,指定返回值的类型。默认为 OBJECT,返回一个 WP_Term 对象。还可以是 ARRAY_A (关联数组), ARRAY_N (索引数组), 或者 OBJECT_K (键为ID的对象数组,WordPress 4.7.0新增)。
  • $filter:可选参数,指定如何过滤返回值。默认为 ‘raw’,不进行任何过滤。

2. 源码剖析:一步一步揭秘

接下来,让我们深入 get_term_by() 函数的源码,看看它是如何工作的。为了方便理解,我将代码拆解成几个关键步骤,并加上详细的注释。

function get_term_by( string $field, string|int $value, string|array $taxonomy = 'category', string $output = OBJECT, string $filter = 'raw' ): WP_Term|false|null {

    global $wpdb;

    // 1. 参数校验和准备
    $taxonomies = (array) $taxonomy; // 将分类法转换为数组,方便处理
    $single_taxonomy = ( 1 === count( $taxonomies ) ); // 标记是否只有一个分类法
    $taxonomy = reset( $taxonomies ); // 获取第一个分类法,用于某些情况下的单分类法操作

    // 2. 检查缓存
    $term = wp_cache_get( "$field:$value:$taxonomy", 'terms' ); // 尝试从缓存中获取 Term
    if ( false !== $term ) {
        /** This filter is documented in wp-includes/taxonomy.php */
        return apply_filters( 'get_term', $term, $taxonomy, $filter ); // 如果缓存命中,直接返回
    }

    // 3. 构建 SQL 查询语句
    $select = 't.*, tt.*'; // 选择的字段
    $from   = "{$wpdb->terms} AS t INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id"; // 表连接
    $where  = $wpdb->prepare( 'tt.taxonomy IN (%s)', implode( "','", array_map( 'esc_sql', $taxonomies ) ) ); // 分类法限制

    switch ( $field ) { // 根据不同的 $field 构建不同的 WHERE 子句
        case 'id':
            $id = intval( $value );
            $where .= $wpdb->prepare( ' AND t.term_id = %d', $id );
            break;
        case 'slug':
            $slug = sanitize_title( $value );
            $where .= $wpdb->prepare( ' AND t.slug = %s', $slug );
            break;
        case 'name':
            $name = trim( $value );
            $where .= $wpdb->prepare( ' AND t.name = %s', $name );
            break;
        case 'term_taxonomy_id':
            $term_taxonomy_id = intval( $value );
            $where .= $wpdb->prepare( ' AND tt.term_taxonomy_id = %d', $term_taxonomy_id );
            break;
        default:
            return false; // 如果 $field 不支持,返回 false
    }

    $orderby = 't.name ASC'; // 默认排序

    $query = "SELECT $select FROM $from WHERE $where ORDER BY $orderby"; // 完整的 SQL 查询语句

    // 4. 执行查询
    $term = $wpdb->get_row( $query ); // 执行查询,获取结果

    if ( ! $term ) {
        return false; // 如果没有找到 Term,返回 false
    }

    // 5. 处理结果
    $term = sanitize_term( $term, $taxonomy, $filter ); // 对 Term 进行清理和过滤
    $term = new WP_Term( $term );

    wp_cache_set( "$field:$value:$taxonomy", $term, 'terms' ); // 将 Term 存入缓存

    $term = apply_filters( 'get_term', $term, $taxonomy, $filter ); // 应用 'get_term' 过滤器

    // 6. 格式化输出
    if ( OBJECT === $output ) {
        return $term; // 返回 WP_Term 对象
    } elseif ( ARRAY_A === $output ) {
        return get_object_vars( $term ); // 返回关联数组
    } elseif ( ARRAY_N === $output ) {
        return array_values( get_object_vars( $term ) ); // 返回索引数组
    } elseif ( OBJECT_K === $output ) {
        return array( $term->term_id => $term );
    }

    return $term; // 默认返回 WP_Term 对象
}

3. 代码解读:逐行分析

  • 参数校验和准备: 首先,函数会将传入的 $taxonomy 参数转换为数组,即使你只传入了一个分类法,也会被转换成数组,方便后续的统一处理。 还会判断是不是单分类,方便后续逻辑处理。
  • 缓存检查: 这是提高性能的关键一步。函数会尝试从 WordPress 的对象缓存中获取 Term。缓存的键是根据 $field, $value, 和 $taxonomy 生成的。如果缓存命中,直接返回缓存中的 Term,避免重复查询数据库。
  • 构建 SQL 查询语句: 这是整个函数的核心部分。函数会根据传入的 $field 参数,动态构建 SQL 查询语句的 WHERE 子句。
    • $select:指定要查询的字段,包括 terms 表和 term_taxonomy 表中的字段。
    • $from:指定要查询的表,这里使用了 INNER JOIN 连接 terms 表和 term_taxonomy 表。
    • $where:这是最重要的部分。函数会根据 $field 的值,构建不同的 WHERE 子句。
      • 如果 $field 是 ‘id’,则查询 t.term_id 字段。
      • 如果 $field 是 ‘slug’,则查询 t.slug 字段。
      • 如果 $field 是 ‘name’,则查询 t.name 字段。
      • 如果 $field 是 ‘term_taxonomy_id’,则查询 tt.term_taxonomy_id 字段。
      • 如果 $field 是其他值,则返回 false,表示不支持该字段的搜索。
  • 执行查询: 使用 $wpdb->get_row() 函数执行 SQL 查询,获取结果。
  • 处理结果: 如果查询结果不为空,则会对 Term 进行清理和过滤,防止 XSS 攻击。还会构建 WP_Term 对象。
  • 存入缓存: 将查询结果存入缓存,以便下次使用。
  • 应用过滤器: 应用 ‘get_term’ 过滤器,允许其他插件或主题修改 Term 对象。
  • 格式化输出: 根据 $output 参数,将 Term 对象格式化为不同的类型,包括 OBJECT(默认),ARRAY_A(关联数组),ARRAY_N(索引数组),或者 OBJECT_K (键为ID的对象数组)。

4. 动态构建查询条件:$wpdb->prepare() 的妙用

在构建 SQL 查询语句的过程中,$wpdb->prepare() 函数起到了至关重要的作用。它主要有两个作用:

  • 防止 SQL 注入: $wpdb->prepare() 函数会对传入的参数进行转义,防止 SQL 注入攻击。
  • 格式化 SQL 语句: $wpdb->prepare() 函数可以根据参数类型,自动格式化 SQL 语句。

例如,以下代码:

$id = intval( $value );
$where .= $wpdb->prepare( ' AND t.term_id = %d', $id );

$wpdb->prepare() 函数会将 $id 参数转换为整数,并将其插入到 SQL 语句中。这样可以确保 SQL 语句的安全性,并提高查询效率。

5. 实际应用:代码示例

为了更好地理解 get_term_by() 函数的用法,我们来看几个实际的代码示例。

  • 根据 ID 获取分类术语:
$term = get_term_by( 'id', 10, 'category' );

if ( $term ) {
    echo '分类术语名称:' . $term->name;
} else {
    echo '未找到分类术语';
}
  • 根据 Slug 获取分类术语:
$term = get_term_by( 'slug', 'uncategorized', 'category' );

if ( $term ) {
    echo '分类术语ID:' . $term->term_id;
} else {
    echo '未找到分类术语';
}
  • 根据 Name 获取分类术语:
$term = get_term_by( 'name', '未分类', 'category' );

if ( $term ) {
    echo '分类术语Slug:' . $term->slug;
} else {
    echo '未找到分类术语';
}
  • 在多个分类法中搜索:
$taxonomies = array( 'category', 'post_tag' );
$term = get_term_by( 'name', '测试', $taxonomies );

if ( $term ) {
    echo '分类术语所属分类法:' . $term->taxonomy;
} else {
    echo '未找到分类术语';
}

6. 注意事项:使用技巧

  • 优先使用 ID 搜索: 如果你知道分类术语的 ID,最好使用 ID 进行搜索,因为 ID 是唯一的,查询效率最高。
  • 注意 Slug 的大小写: Slug 是区分大小写的,所以要确保你提供的 Slug 与数据库中的 Slug 完全一致。
  • 利用缓存: get_term_by() 函数会自动使用缓存,但如果你需要频繁地获取同一个分类术语,可以手动使用 wp_cache_get()wp_cache_set() 函数来管理缓存,以提高性能。
  • 理解 $output 参数: 根据你的需求选择合适的 $output 参数。如果你只需要分类术语的少量信息,可以使用 ARRAY_AARRAY_N,这样可以减少内存占用。
  • 注意 $filter 参数: 默认情况下,get_term_by() 函数会对分类术语进行清理和过滤。如果你需要获取原始的分类术语数据,可以将 $filter 参数设置为 ‘raw’。

7. get_term_by()get_term() 的区别

有些朋友可能会问,get_term_by()get_term() 函数有什么区别呢?

函数 功能 参数 返回值
get_term_by() 根据指定的字段和值,获取分类术语 $field (字段名), $value (字段值), $taxonomy (分类法), $output (输出格式), $filter (过滤器) WP_Term 对象, false (未找到), null (出错), 或者根据 $output 参数返回不同的格式
get_term() 根据分类术语的 ID 或 Slug,获取分类术语 $term (Term ID 或 Term Object), $taxonomy (分类法), $output (输出格式), $filter (过滤器) WP_Term 对象, false (未找到), WP_Error 对象 (出错), 或者根据 $output 参数返回不同的格式。 如果传入的 $term 是一个 WP_Error 对象,函数会直接返回该对象。

简单来说,get_term_by() 函数更加灵活,可以根据不同的字段进行搜索,而 get_term() 函数只能根据 ID 或 Slug 进行搜索。

8. 总结:get_term_by() 的价值

get_term_by() 函数是 WordPress 中一个非常实用且功能强大的函数。它允许你根据不同的条件,动态地查询和获取分类术语。通过深入理解其源码,我们可以更好地掌握其用法,并利用它来构建更高效、更灵活的 WordPress 应用程序。

希望今天的分享对大家有所帮助。如果有什么疑问,欢迎随时提问!咱们下期再见!

发表回复

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