探究 WordPress `get_terms()` 函数的源码:如何通过 `WP_Term_Query` 类查询分类术语。

各位观众老爷们,晚上好!今天咱们来聊聊 WordPress 的 get_terms() 函数,这玩意儿用起来挺方便,但背地里其实调用了一个更强大的类——WP_Term_Query。咱们一起扒一扒它的源码,看看它是怎么把数据库里的分类术语(categories, tags, etc.)给揪出来的。

get_terms():门面担当

首先,咱们得对 get_terms() 有个大致了解。这函数就像个友好的服务员,你告诉它想吃啥(参数),它就去厨房(数据库)给你端上来。

简单来说,get_terms() 的作用就是根据你提供的参数,从数据库中获取分类术语(Term)。这些参数可以包括:

  • taxonomy:指定要查询的分类法(例如:’category’,’post_tag’)。
  • hide_empty:是否隐藏没有文章的术语。
  • orderby:按什么排序(例如:’name’,’count’)。
  • order:升序还是降序(’ASC’,’DESC’)。
  • number:返回多少个术语。
  • offset:从第几个术语开始返回。
  • search:搜索匹配的术语名称。
  • slug:通过slug查找术语。
  • term_taxonomy_id:通过term_taxonomy_id查找术语。

等等等等,一大堆。

<?php
// 举个栗子:获取所有 category 分类下的术语,按名称升序排列
$terms = get_terms( array(
    'taxonomy' => 'category',
    'orderby'  => 'name',
    'order'    => 'ASC',
    'hide_empty' => false, //显示空的分类
) );

if ( ! empty( $terms ) && ! is_wp_error( $terms ) ){
    echo '<ul>';
    foreach ( $terms as $term ) {
        echo '<li>' . esc_html( $term->name ) . '</li>';
    }
    echo '</ul>';
}
?>

这段代码的作用就是从 'category' 分类法中取出所有术语,按照名称升序排列,并显示出来。

WP_Term_Query:幕后英雄

现在,重点来了。get_terms() 函数真正干活的,其实是 WP_Term_Query 类。 它就像厨房里的主厨,负责把服务员点的菜变成现实。

让我们一起潜入 wp-includes/class-wp-term-query.php 文件,看看这主厨是怎么工作的。

首先,找到 WP_Term_Query 类的定义。你会发现它继承了一个名为 WP_Query 的类,而 WP_Query 是 WordPress 中用于查询各种数据的核心类。 WP_Term_Query 特化了 WP_Query,专门用于查询分类术语。

WP_Term_Query::__construct():初始化查询参数

WP_Term_Query 类的构造函数 __construct() 负责接收查询参数,并进行一些必要的预处理。

public function __construct( $query = '' ) {
    if ( ! empty( $query ) ) {
        $this->query( $query );
    }
}

很简单,如果传入了查询参数 $query,就调用 query() 方法来处理。

WP_Term_Query::query():参数解析与处理

query() 方法是整个查询流程的核心。它接收一个参数数组,然后将这些参数转换为 SQL 查询语句。

public function query( $query ) {
    $this->query_vars = wp_parse_args( $query, $this->defaults );
    return $this->get_terms();
}

这里做了两件事:

  1. wp_parse_args():这个函数将传入的参数 $query 与默认参数 $this->defaults 合并。$this->defaults 定义了 WP_Term_Query 的默认参数值。 这样可以确保所有必要的参数都有值,即使调用者没有显式指定。
  2. $this->get_terms():调用 get_terms() 方法执行实际的查询操作。

WP_Term_Query::get_terms():执行查询

get_terms() 方法负责根据查询参数构建 SQL 查询语句,并从数据库中获取结果。

public function get_terms() {
    global $wpdb;

    //... 省略一些钩子和缓存相关的代码 ...

    $fields = $this->parse_fields(); //解析查询字段

    $clauses = $this->get_sql_clauses(); //构建 SQL 查询语句

    $this->sql = "SELECT $fields FROM {$wpdb->terms} AS t {$clauses['join']} WHERE 1=1 {$clauses['where']} {$clauses['orderby']} {$clauses['limits']}";

    //... 省略一些调试和缓存相关的代码 ...

    $terms = $wpdb->get_results( $this->sql );

    //... 省略一些结果处理和对象缓存相关的代码 ...

    return $terms;
}

这段代码的关键步骤:

  1. $this->parse_fields():确定要查询哪些字段。默认情况下,查询所有字段。
  2. $this->get_sql_clauses():构建 SQL 查询语句的各个部分,包括 JOINWHEREORDER BYLIMIT 子句。
  3. $this->sql = ...:将各个部分拼接成完整的 SQL 查询语句。
  4. $wpdb->get_results( $this->sql ):执行 SQL 查询,并返回结果。

WP_Term_Query::parse_fields():确定查询字段

这个方法决定了 SQL 查询语句中 SELECT 后面跟着什么。

protected function parse_fields() {
    if ( isset( $this->query_vars['fields'] ) ) {
        $fields = $this->query_vars['fields'];
    } else {
        $fields = 'all';
    }

    if ( 'all' == $fields ) {
        return 't.*, tt.*'; //查询terms表和term_taxonomy表的所有字段
    } elseif ( 'ids' == $fields ) {
        return 't.term_id'; //只查询term_id
    } elseif ( 'names' == $fields ) {
        return 't.name'; //只查询name
    } elseif ( 'count' == $fields ) {
        return 'COUNT(*)'; //查询数量
    } else {
        return 't.*, tt.*'; //默认查询所有字段
    }
}

根据 $this->query_vars['fields'] 的值,返回不同的字段列表。

WP_Term_Query::get_sql_clauses():构建 SQL 子句

这是整个查询过程中最复杂的部分。这个方法负责根据查询参数构建 SQL 查询语句的各个子句,例如 JOINWHEREORDER BYLIMIT

protected function get_sql_clauses() {
    global $wpdb;

    $orderby = $this->parse_orderby();

    $order = strtoupper( $this->query_vars['order'] );

    if ( ! in_array( $order, array( 'ASC', 'DESC' ), true ) ) {
        $order = 'ASC';
    }

    $number  = absint( $this->query_vars['number'] );
    $offset  = absint( $this->query_vars['offset'] );
    $limits  = '';

    if ( ! empty( $number ) ) {
        if ( empty( $offset ) ) {
            $limits = "LIMIT $number";
        } else {
            $limits = "LIMIT $offset, $number";
        }
    }

    $where = '';
    $join  = '';

    // taxonomy参数,用于筛选特定的分类法
    if ( ! empty( $this->query_vars['taxonomy'] ) ) {
        $taxonomies = wp_parse_slug_list( $this->query_vars['taxonomy'] );

        if ( $taxonomies ) {
            $taxonomies = esc_sql( $taxonomies );
            $where .= " AND tt.taxonomy IN ('" . implode( "', '", $taxonomies ) . "')";
        }
    }

    // hide_empty参数,用于隐藏没有文章的术语
    if ( $this->query_vars['hide_empty'] && 'count' !== $this->query_vars['fields'] ) {
        $where .= ' AND tt.count > 0';
    }

    // ...省略了处理其他参数的代码...

    //orderby子句
    $orderby_clause = '';
    if ( ! empty( $orderby ) ) {
        $orderby_clause = 'ORDER BY ' . implode( ', ', $orderby );
    }

    return compact( 'where', 'join', 'orderby_clause', 'limits' );
}

这个方法做了很多事情,包括:

  • 处理 orderbyorder 参数,构建 ORDER BY 子句。
  • 处理 numberoffset 参数,构建 LIMIT 子句。
  • 处理 taxonomy 参数,构建 WHERE 子句,用于筛选特定的分类法。
  • 处理 hide_empty 参数,构建 WHERE 子句,用于隐藏没有文章的术语。
  • 处理其他各种参数,构建相应的 SQL 子句。

WP_Term_Query::parse_orderby():解析排序参数

这个方法用于解析 orderby 参数,并将其转换为 SQL 查询语句中的 ORDER BY 子句。

protected function parse_orderby() {
    global $wpdb;

    $orderby = $this->query_vars['orderby'];

    if ( ! is_array( $orderby ) ) {
        $orderby = preg_split( '/[,s]+/', $orderby );
    }

    $parsed_orderby = array();

    foreach ( $orderby as $term ) {
        $term = strtolower( $term );
        switch ( $term ) {
            case 'name':
                $parsed_orderby[] = 't.name';
                break;
            case 'slug':
                $parsed_orderby[] = 't.slug';
                break;
            case 'term_group':
                $parsed_orderby[] = 't.term_group';
                break;
            case 'term_id':
                $parsed_orderby[] = 't.term_id';
                break;
            case 'id':
                $parsed_orderby[] = 't.term_id';
                break;
            case 'count':
                $parsed_orderby[] = 'tt.count';
                break;
            case 'term_order':
                $parsed_orderby[] = 'tt.term_order';
                break;
            default:
                 //... 省略一些处理自定义排序字段的代码 ...
                break;
        }
    }

    return $parsed_orderby;
}

这个方法根据 orderby 参数的值,将其转换为数据库字段名。例如,如果 orderby 的值为 'name',则将其转换为 't.name'

总结:get_terms()WP_Term_Query 的关系

简单来说,get_terms() 是一个方便的函数,用于获取分类术语。它内部使用 WP_Term_Query 类来构建和执行 SQL 查询。 WP_Term_Query 类提供了更强大的查询功能,允许你使用各种参数来筛选和排序结果。

用表格来总结一下:

函数/类 作用 关系
get_terms() 提供一个方便的接口,用于获取分类术语。 调用 WP_Term_Query 类来执行查询。
WP_Term_Query 构建 SQL 查询语句,从数据库中获取分类术语。 get_terms() 函数的幕后英雄。

实际应用场景

了解了 get_terms()WP_Term_Query 的原理,我们就可以更灵活地使用它们。

例如,假设我们需要获取所有 'category' 分类下的术语,但是要排除 ID 为 1 和 2 的术语。我们可以这样做:

<?php
$args = array(
    'taxonomy' => 'category',
    'exclude'  => array( 1, 2 ),
);

$terms = get_terms( $args );

if ( ! empty( $terms ) && ! is_wp_error( $terms ) ){
    echo '<ul>';
    foreach ( $terms as $term ) {
        echo '<li>' . esc_html( $term->name ) . '</li>';
    }
    echo '</ul>';
}
?>

这里我们使用了 exclude 参数,用于排除指定的术语。

更高级的用法

如果你需要更精细的控制查询过程,你可以直接使用 WP_Term_Query 类。例如,你可以自定义 SQL 查询语句的 WHERE 子句。

<?php
$args = array(
    'taxonomy' => 'category',
    'name__like' => 'WordPress', // 查找名称包含 "WordPress" 的分类
);

$term_query = new WP_Term_Query( $args );

$terms = $term_query->get_terms();

if ( ! empty( $terms ) ) {
    echo '<ul>';
    foreach ( $terms as $term ) {
        echo '<li>' . esc_html( $term->name ) . '</li>';
    }
    echo '</ul>';
}
?>

这个例子使用了 name__like 参数,用于查找名称包含 'WordPress' 的分类。

总结

好了,各位观众老爷们,今天我们一起深入研究了 WordPress 的 get_terms() 函数和 WP_Term_Query 类。希望通过今天的讲解,大家对 WordPress 的分类术语查询有了更深入的了解。下次再使用 get_terms() 的时候,心里就不会发虚了,因为你知道它背后的原理了。 记住,掌握了这些底层原理,才能更好地驾驭 WordPress 这匹野马!

下次再见!

发表回复

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