咳咳,各位同学们,老司机要开车了,今天咱们来聊聊 WordPress 里面的 WP_Term_Query
这个类,尤其是它怎么通过 taxonomy
参数来构建 SQL 语句查询分类术语的。这玩意儿看起来高深莫测,其实扒开皮儿,你会发现它也就那么回事儿。准备好了吗?咱们开始!
一、WP_Term_Query
是个啥?
简单来说,WP_Term_Query
是 WordPress 提供的一个专门用来查询分类术语(也就是 taxonomy terms,例如分类目录、标签等等)的类。它允许你使用各种各样的参数来过滤、排序、分页你的术语。你想按名称查,按描述查,按 parent 查,按 slug 查,甚至按 term_id 查,它都能满足你。
二、taxonomy
参数:核心中的核心
taxonomy
参数,顾名思义,指定你要查询哪个或哪些分类法。这是 WP_Term_Query
必须处理的核心参数之一。 如果没有这个参数,WP_Term_Query
就不知道你要查什么类型的术语,查询就无从谈起。
三、源码解剖:一步一步看它怎么干的
咱们直接上代码,从 WP_Term_Query
的构造函数开始,看看它如何处理 taxonomy
参数并构建 SQL 语句。
<?php
// 假设这是简化版的 WP_Term_Query 类 (为了方便讲解,省略了一些不重要的部分)
class My_WP_Term_Query {
public $query_vars = array(); //存储查询参数
public $terms; // 查询结果
public $term_count; // 查询到的术语数量
public $sql_clauses = array(
'select' => '',
'from' => '',
'where' => '',
'orderby' => '',
'limits' => '',
);
public function __construct( $query = '' ) {
$this->query_vars = wp_parse_args( $query, $this->get_default_query() );
// 处理 taxonomy 参数
$this->parse_taxonomies();
// 构建 SQL
$this->get_terms();
}
protected function get_default_query() {
return array(
'taxonomy' => 'category', // 默认分类法
'object_ids' => null,
'number' => '',
'offset' => '',
'orderby' => 'name',
'order' => 'ASC',
'hide_empty' => false,
'include' => array(),
'exclude' => array(),
'search' => '',
'cache_domain' => 'core',
);
}
protected function parse_taxonomies() {
$taxonomies = $this->query_vars['taxonomy'];
if ( ! is_array( $taxonomies ) ) {
$taxonomies = preg_split( '/[,s]+/', $taxonomies );
}
$this->query_vars['taxonomy'] = $taxonomies;
}
public function get_terms() {
global $wpdb;
$this->sql_clauses['select'] = 'SELECT t.*, tt.* ';
$this->sql_clauses['from'] = "FROM {$wpdb->terms} AS t INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id ";
$this->sql_clauses['where'] = 'WHERE 1=1 ';
$this->sql_clauses['orderby'] = 'ORDER BY t.name ASC';
$this->sql_clauses['limits'] = '';
// 添加 taxonomy 条件
$this->sql_clauses['where'] .= $this->get_taxonomy_sql();
// 构建完整的 SQL 语句
$sql = $this->sql_clauses['select'] .
$this->sql_clauses['from'] .
$this->sql_clauses['where'] .
$this->sql_clauses['orderby'] .
$this->sql_clauses['limits'];
// 查询数据库
$this->terms = $wpdb->get_results( $sql );
$this->term_count = count($this->terms);
return $this->terms;
}
protected function get_taxonomy_sql() {
global $wpdb;
$taxonomies = $this->query_vars['taxonomy'];
$taxonomies = array_map( 'esc_sql', $taxonomies );
$taxonomy_list = "'" . implode( "', '", $taxonomies ) . "'";
$sql = "AND tt.taxonomy IN ( {$taxonomy_list} )";
return $sql;
}
}
// 使用示例
$args = array(
'taxonomy' => array( 'category', 'post_tag' ),
'hide_empty' => true,
'orderby' => 'count',
'order' => 'DESC'
);
$term_query = new My_WP_Term_Query( $args );
if ( $term_query->terms ) {
echo "找到 " . $term_query->term_count . " 个术语n";
foreach ( $term_query->terms as $term ) {
echo "Term: " . $term->name . ", Taxonomy: " . $term->taxonomy . "n";
}
} else {
echo "没有找到任何术语n";
}
?>
代码解读:
-
构造函数
__construct()
:- 接收一个查询参数数组
$query
。 - 使用
wp_parse_args()
函数将传入的参数与默认参数合并。 这样,即使你只传入了taxonomy
参数,其他参数也会有默认值。 - 调用
parse_taxonomies()
方法来确保taxonomy
参数是数组形式。 如果传入的是字符串(例如'category'
),则将其转换为数组。 - 调用
get_terms()
执行查询
- 接收一个查询参数数组
-
get_default_query()
:- 返回一个包含默认查询参数的数组。 关键的是
'taxonomy' => 'category'
,这意味着如果你不指定taxonomy
,默认会查询category
(分类目录)。
- 返回一个包含默认查询参数的数组。 关键的是
-
parse_taxonomies()
:- 处理
taxonomy
参数,确保它是一个数组。 这样,即使你传入的是用逗号分隔的字符串(例如'category, post_tag'
),也会被转换为数组array('category', 'post_tag')
。
- 处理
-
get_terms()
:- 构建 SQL 查询语句的核心方法。
- 首先定义基本的 SQL 语句框架,包括
SELECT
、FROM
、WHERE
、ORDER BY
和LIMIT
子句。 注意这里用到了$wpdb
全局对象,它是 WordPress 数据库操作的核心。 - 调用
get_taxonomy_sql()
方法来生成WHERE
子句中关于taxonomy
的条件。 - 将各个 SQL 子句拼接成完整的 SQL 语句。
- 使用
$wpdb->get_results()
函数执行 SQL 查询,并将结果存储在$this->terms
属性中。
-
get_taxonomy_sql()
:- 根据
taxonomy
参数生成 SQL 的WHERE
子句。 - 将
taxonomy
数组中的每个元素使用esc_sql()
函数进行转义,防止 SQL 注入。 - 将转义后的 taxonomy 列表用逗号连接,并用单引号括起来。
- 构建
AND tt.taxonomy IN ( {$taxonomy_list} )
这样的 SQL 片段,用于筛选指定的分类法。
- 根据
四、SQL 语句示例
假设我们使用以下参数:
$args = array(
'taxonomy' => array( 'category', 'post_tag' ),
'hide_empty' => true,
'orderby' => 'count',
'order' => 'DESC'
);
那么,最终生成的 SQL 语句可能是这样的(简化版):
SELECT t.*, tt.*
FROM wp_terms AS t
INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id
WHERE 1=1
AND tt.taxonomy IN ( 'category', 'post_tag' )
ORDER BY t.name ASC
解释:
SELECT t.*, tt.*
:选择wp_terms
表和wp_term_taxonomy
表的所有字段。FROM wp_terms AS t INNER JOIN wp_term_taxonomy AS tt ON t.term_id = tt.term_id
:从wp_terms
表和wp_term_taxonomy
表进行内连接,连接条件是term_id
相等。WHERE 1=1 AND tt.taxonomy IN ( 'category', 'post_tag' )
:WHERE
子句,1=1
是一个常用的技巧,方便后续添加条件。tt.taxonomy IN ( 'category', 'post_tag' )
筛选出taxonomy
为'category'
或'post_tag'
的术语。ORDER BY t.name ASC
: 根据t.name
也就是term的name排序,升序
五、深入理解:WP_Term_Query
的设计思想
- 灵活性:
WP_Term_Query
的设计非常灵活,允许你通过各种参数来定制你的查询。 - 可扩展性: 你可以通过 filter 钩子来修改 SQL 语句,从而实现更复杂的查询需求。 例如,你可以添加自定义的
WHERE
子句,或者修改ORDER BY
子句。 - 安全性:
WP_Term_Query
使用esc_sql()
函数来转义 SQL 语句中的参数,防止 SQL 注入。
六、WP_Term_Query
的其他重要参数
除了 taxonomy
之外,WP_Term_Query
还有很多其他重要的参数,它们共同作用,构建出强大的查询能力。 下面是一些常用的参数:
参数 | 描述 | 示例 |
---|---|---|
object_ids |
根据文章ID筛选关联的术语。 | $args['object_ids'] = array(1, 2, 3); // 获取与文章ID 1, 2, 3 关联的术语 |
number |
返回术语的最大数量。 | $args['number'] = 5; // 只返回5个术语 |
offset |
从查询结果的哪个位置开始返回术语。 | $args['offset'] = 10; // 跳过前10个术语,从第11个开始返回 |
orderby |
排序字段。 可以是 ‘name’(默认)、’count’、’term_id’、’slug’ 等。 | $args['orderby'] = 'count'; // 按术语计数排序 |
order |
排序方式。 可以是 ‘ASC’(默认,升序)或 ‘DESC’(降序)。 | $args['order'] = 'DESC'; // 降序排列 |
hide_empty |
是否隐藏没有文章的术语。 默认值为 false 。 |
$args['hide_empty'] = true; // 隐藏没有文章的术语 |
include |
只返回指定的术语 ID。 | $args['include'] = array(1, 2, 3); // 只返回 ID 为 1, 2, 3 的术语 |
exclude |
排除指定的术语 ID。 | $args['exclude'] = array(4, 5, 6); // 排除 ID 为 4, 5, 6 的术语 |
slug |
根据术语的 slug 进行筛选。 可以是单个 slug,也可以是 slug 数组。 | $args['slug'] = 'my-term'; // 只返回 slug 为 ‘my-term’ 的术语 $args['slug'] = array('my-term', 'another-term'); // 返回 slug 为 ‘my-term’ 或 ‘another-term’ 的术语 |
search |
搜索术语的名称或描述。 | $args['search'] = 'keyword'; // 搜索名称或描述包含 ‘keyword’ 的术语 |
name__like |
匹配类似特定名称的术语。 | $args['name__like'] = 'key'; // 搜索名称类似 ‘key’ 的术语 |
description__like |
匹配类似特定描述的术语。 | $args['description__like'] = 'key'; // 搜索描述类似 ‘key’ 的术语 |
term_id |
根据term_id进行筛选,可以是单个term_id,也可以是term_id数组 | $args['term_id'] = 1; //只返回ID为1的术语 $args['term_id'] = array(1, 2, 3); // 只返回 ID 为 1, 2, 3 的术语 |
parent |
只返回指定父级术语ID的子术语 | $args['parent'] = 10; //只返回父级term_id为10的术语 |
hierarchical |
是否以层级结构返回术语。默认值为 true | $args['hierarchical'] = false; //不以层级结构返回术语 |
七、实战案例:复杂查询
假设我们需要查询 category
和 post_tag
这两种分类法下,名称包含 "WordPress",并且至少关联了 5 篇文章的术语,按照文章数量降序排列,并且排除 ID 为 10 的术语。
$args = array(
'taxonomy' => array( 'category', 'post_tag' ),
'search' => 'WordPress',
'hide_empty' => true, // 确保至少关联了一篇文章
'orderby' => 'count',
'order' => 'DESC',
'exclude' => array( 10 ),
'number' => 10, // 最多返回10个
'hierarchical' => false //不以层级结构返回
);
$term_query = new My_WP_Term_Query( $args );
if ( $term_query->terms ) {
foreach ( $term_query->terms as $term ) {
echo "Term: " . $term->name . ", Count: " . $term->count . "n";
}
} else {
echo "没有找到任何术语n";
}
八、总结:WP_Term_Query
的力量
WP_Term_Query
是 WordPress 中一个非常强大的类,它可以让你轻松地查询和管理分类术语。 掌握 WP_Term_Query
,你就可以构建出更加灵活和强大的 WordPress 应用。
今天的课就到这里,希望大家对 WP_Term_Query
有了更深入的了解。 下课!