剖析 `get_term_by()` 函数的源码,它如何根据不同的字段类型(如 `slug`、`name`)来查询分类术语。

各位老铁,早上好!今天咱们来聊聊 WordPress 里面一个神奇的函数:get_term_by()。这玩意儿就像个万能钥匙,能根据各种条件帮你找到想要的分类术语(taxonomy term)。但它到底是怎么做到的呢?别急,咱们今天就扒开它的源码,看看里面藏着什么乾坤。

Part 1: 开场白和函数概览

在WordPress的世界里,taxonomy 就是分类法,比如 category(分类)、tag(标签) 等。而 term 则是 taxonomy 下面的具体条目,比如一个 category 叫 "科技",一个 tag 叫 "WordPress"。

get_term_by() 的作用就是根据你给定的条件(比如 slug、name、id),去数据库里把对应的 term 找出来。

简单来说,它的基本用法是这样的:

<?php
$term = get_term_by( 'slug', 'my-awesome-category', 'category' );

if ( $term ) {
    echo 'Category Name: ' . $term->name;
} else {
    echo 'Category not found!';
}
?>

这个例子就是根据 slug ‘my-awesome-category’ 在 ‘category’ 这个 taxonomy 下查找对应的 term。如果找到了,就输出它的名字。

Part 2: 源码剖析(核心部分)

好了,废话不多说,直接上源码。以下是 get_term_by() 函数的核心部分(为了方便理解,我对源码做了一些简化和注释):

function get_term_by( $field, $value, $taxonomy, $output = OBJECT, $filter = 'raw' ) {
    global $wpdb;

    $taxonomy = sanitize_taxonomy( $taxonomy ); // 确保 taxonomy 是合法的

    if ( ! taxonomy_exists( $taxonomy ) ) { // 检查 taxonomy 是否存在
        return false;
    }

    $id = false;

    // 根据不同的 $field 进行不同的查询
    switch ( $field ) {
        case 'id':
        case 'term_id':
            $id = intval( $value ); // 转换为整数
            if ( empty( $id ) ) {
                return false;
            }
            break;
        case 'slug':
            $slug = sanitize_title( $value ); // 对 slug 进行安全处理
            if ( empty( $slug ) ) {
                return false;
            }

            $term = get_term( $slug, $taxonomy ); // 尝试用 get_term 直接获取
            if ( $term && ! is_wp_error( $term ) ) {
                return $term;
            }

            $id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE slug = %s", $slug ) ); // 查询 term_id
            if ( empty( $id ) ) {
                return false;
            }
            break;
        case 'name':
            $name = trim( $value ); // 去除首尾空格
            if ( '' === $name ) {
                return false;
            }

            $id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE name = %s", $name ) ); // 查询 term_id
            if ( empty( $id ) ) {
                return false;
            }
            break;
        case 'term_taxonomy_id':
            $term_taxonomy_id = intval( $value );
            if ( empty( $term_taxonomy_id ) ) {
                return false;
            }
            $term_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) );

            if ( empty( $term_id ) ) {
                return false;
            }
            $id = $term_id;
            break;
        default:
            return false; // 不支持的 $field
    }

    if ( $id ) {
        $term = get_term( $id, $taxonomy, $output, $filter ); // 通过 term_id 获取 term 对象
        if ( is_wp_error( $term ) ) {
            return false;
        }
        return $term;
    }

    return false;
}

看完这段代码,是不是感觉有点眼花缭乱?别怕,咱们一步一步来分析。

2.1 参数校验和准备

首先,函数接收几个参数:

  • $field: 要查询的字段,比如 ‘id’, ‘slug’, ‘name’ 等。
  • $value: $field 对应的值,比如 slug 是 ‘my-awesome-category’。
  • $taxonomy: 分类法的名称,比如 ‘category’, ‘post_tag’。
  • $output: 输出的格式,默认是 OBJECT(对象),还可以是 ARRAY_A(关联数组)或 ARRAY_N(数字索引数组)。
  • $filter: 对 term 数据进行过滤的方式,默认是 ‘raw’。

函数一开始会对 $taxonomy 进行安全处理,并检查它是否存在。如果 taxonomy 不存在,直接返回 false

2.2 根据 $field 选择查询方式(核心逻辑)

这部分是整个函数的核心,它根据 $field 的不同,采取不同的查询策略。

  • $field 是 ‘id’ 或 ‘term_id’

    这是最简单的情况,直接把 $value 转换成整数 $id,然后跳到最后的 get_term() 函数去获取 term 对象。

    case 'id':
    case 'term_id':
        $id = intval( $value ); // 转换为整数
        if ( empty( $id ) ) {
            return false;
        }
        break;
  • $field 是 ‘slug’

    这种情况稍微复杂一点。首先,它会对 $value (也就是 slug) 进行安全处理。然后,它会尝试直接使用 get_term() 函数来获取 term。如果 get_term() 能够成功获取 term,就直接返回。

    为什么这里要先用 get_term() 尝试一下呢?因为 get_term() 内部有缓存机制,如果之前已经获取过这个 term,就可以直接从缓存中取,而不用再去查询数据库,提高效率。

    如果 get_term() 没有成功获取 term,那说明这个 term 可能不在缓存里,或者根本不存在。这时候,就需要去数据库里查询 term_id

    case 'slug':
        $slug = sanitize_title( $value ); // 对 slug 进行安全处理
        if ( empty( $slug ) ) {
            return false;
        }
    
        $term = get_term( $slug, $taxonomy ); // 尝试用 get_term 直接获取
        if ( $term && ! is_wp_error( $term ) ) {
            return $term;
        }
    
        $id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE slug = %s", $slug ) ); // 查询 term_id
        if ( empty( $id ) ) {
            return false;
        }
        break;

    这里用到了 $wpdb->prepare() 函数,这是 WordPress 推荐的安全的 SQL 查询方式,可以防止 SQL 注入攻击。

    $wpdb->get_var() 函数用于执行 SQL 查询,并返回查询结果的第一行第一列的值,也就是 term_id

  • $field 是 ‘name’

    这种情况和 ‘slug’ 类似,也是先对 $value (也就是 name) 进行安全处理,然后去数据库里查询 term_id

    case 'name':
        $name = trim( $value ); // 去除首尾空格
        if ( '' === $name ) {
            return false;
        }
    
        $id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE name = %s", $name ) ); // 查询 term_id
        if ( empty( $id ) ) {
            return false;
        }
        break;
  • $field 是 ‘term_taxonomy_id’

    这种情况是根据 term_taxonomy_id 来查找 term。term_taxonomy_idwp_term_taxonomy 表中的主键,用于关联 term 和 taxonomy。

    case 'term_taxonomy_id':
        $term_taxonomy_id = intval( $value );
        if ( empty( $term_taxonomy_id ) ) {
            return false;
        }
        $term_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $term_taxonomy_id ) );
    
        if ( empty( $term_id ) ) {
            return false;
        }
        $id = $term_id;
        break;
  • $field 是其他值

    如果 $field 是其他不支持的值,函数直接返回 false

    default:
        return false; // 不支持的 $field

2.3 获取 Term 对象

如果通过上面的查询,成功获取到了 $id (也就是 term_id),那么就可以使用 get_term() 函数来获取对应的 term 对象了。

if ( $id ) {
    $term = get_term( $id, $taxonomy, $output, $filter ); // 通过 term_id 获取 term 对象
    if ( is_wp_error( $term ) ) {
        return false;
    }
    return $term;
}

get_term() 函数会根据 $output 参数,返回不同格式的 term 数据。

Part 3: get_term() 函数的简单介绍

get_term_by() 函数中,最终是通过 get_term() 函数来获取 term 对象的。所以,我们简单了解一下 get_term() 函数。

get_term() 函数的主要作用是:

  1. 从缓存中获取 term 数据: 如果 term 数据已经在缓存中,直接返回缓存中的数据,避免重复查询数据库。
  2. 查询数据库获取 term 数据: 如果 term 数据不在缓存中,就查询数据库获取 term 数据。
  3. 对 term 数据进行过滤: 根据 $filter 参数,对 term 数据进行过滤,比如进行 HTML 转义等。
  4. 返回 term 数据: 根据 $output 参数,返回不同格式的 term 数据,比如对象、关联数组或数字索引数组。

Part 4: 数据库表结构

为了更好地理解 get_term_by() 函数的查询过程,我们需要了解一下 WordPress 数据库中与 term 相关的表结构。

主要涉及以下两个表:

  • wp_terms 表: 存储 term 的基本信息,比如 term_idnameslug

    字段 类型 描述
    term_id bigint(20) term 的 ID (主键)
    name varchar(200) term 的名称
    slug varchar(200) term 的 slug (URL 友好型)
    term_group bigint(10) term 的分组 (很少使用)
  • wp_term_taxonomy 表: 存储 term 和 taxonomy 之间的关联关系,以及 term 的统计信息。

    字段 类型 描述
    term_taxonomy_id bigint(20) term taxonomy 的 ID (主键)
    term_id bigint(20) 关联的 term 的 ID
    taxonomy varchar(32) 分类法的名称 (category, post_tag 等)
    description longtext term 的描述
    parent bigint(20) 父级 term 的 ID
    count bigint(20) 使用该 term 的文章数量

Part 5: 总结和注意事项

好了,咱们来总结一下:

  • get_term_by() 函数是一个根据不同字段查找分类术语的万能钥匙。
  • 它会根据 $field 的不同,采取不同的查询策略,包括直接从缓存获取、查询数据库等。
  • get_term_by() 函数内部使用了 get_term() 函数来获取 term 对象。
  • 理解 WordPress 数据库中 wp_termswp_term_taxonomy 表的结构,有助于更好地理解 get_term_by() 函数的查询过程。

注意事项:

  • 在使用 get_term_by() 函数时,一定要注意对 $value 进行安全处理,防止 SQL 注入攻击。
  • 尽量使用 ‘id’ 或 ‘term_id’ 来查询 term,因为这种方式效率最高。
  • 如果需要频繁查询同一个 term,可以考虑使用 wp_cache_set() 函数将 term 数据缓存起来,提高效率。

Part 6: 举个栗子

假设我们有一个 category,它的 name 是 "WordPress 开发",slug 是 "wordpress-development",term_id 是 5。

我们可以使用 get_term_by() 函数来获取这个 category:

<?php
// 根据 term_id 获取
$term_by_id = get_term_by( 'id', 5, 'category' );
if ( $term_by_id ) {
    echo 'Term Name (by ID): ' . $term_by_id->name . '<br>';
}

// 根据 slug 获取
$term_by_slug = get_term_by( 'slug', 'wordpress-development', 'category' );
if ( $term_by_slug ) {
    echo 'Term Name (by Slug): ' . $term_by_slug->name . '<br>';
}

// 根据 name 获取
$term_by_name = get_term_by( 'name', 'WordPress 开发', 'category' );
if ( $term_by_name ) {
    echo 'Term Name (by Name): ' . $term_by_name->name . '<br>';
}
?>

这段代码会输出:

Term Name (by ID): WordPress 开发
Term Name (by Slug): WordPress 开发
Term Name (by Name): WordPress 开发

Part 7: 扩展思考

get_term_by() 函数的源码虽然不复杂,但它体现了一些重要的编程思想:

  • 分层设计: get_term_by() 函数只是一个入口,它会将具体的查询工作委托给其他函数,比如 get_term()
  • 缓存机制: 通过使用缓存,可以避免重复查询数据库,提高效率。
  • 安全性: 使用 $wpdb->prepare() 函数,可以防止 SQL 注入攻击。
  • 灵活性: 通过不同的 $field 参数,可以支持多种查询方式。

这些思想在 WordPress 的其他函数中也经常出现,理解它们有助于我们更好地理解 WordPress 的架构和设计。

Part 8: 结束语

好了,今天的 get_term_by() 函数源码剖析就到这里了。希望通过今天的讲解,大家对这个函数有了更深入的了解。记住,源码是最好的老师,多看源码,多思考,才能真正提高自己的编程水平。下次有机会,咱们再聊聊 WordPress 的其他有趣函数! 溜了溜了~

发表回复

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