咳咳,各位观众老爷们,晚上好!今天咱们来聊聊 WordPress 里的一个“隐藏Boss”—— WP_Term_Meta_Query
类。这哥们儿专门负责处理分类术语元数据的查询,听起来是不是有点枯燥?别急,我保证用最接地气的方式,把这玩意儿扒个底朝天,让你们以后面对它的时候,就像见到老朋友一样亲切。
一、啥是分类术语元数据?
在深入源码之前,咱们先得搞清楚,啥是分类术语元数据。简单来说,就是给你的分类、标签这些玩意儿,加上一些额外的信息。
举个栗子:
- 你有个分类叫“美食攻略”,你想给它加个元数据,说明“这个分类下的文章都是关于吃的”。
- 你有个标签叫“旅行”,你想给它加个元数据,说明“这个标签下的文章适合喜欢冒险的人”。
这些额外的信息,就叫做分类术语元数据。 它们存储在 wp_termmeta
表里。
二、WP_Term_Meta_Query
:元数据查询的幕后英雄
WP_Term_Meta_Query
类是 WordPress 用来处理 wp_termmeta
表查询的核心类。 它可以让你像操作文章元数据一样,使用灵活的查询条件来获取特定的分类术语元数据。
三、源码剖析:WP_Term_Meta_Query
的内部构造
咱们现在就来深入 WP_Term_Meta_Query
的源码,看看它到底是怎么工作的。
-
构造函数
__construct( $meta_query = false )
这是类的“出生点”,负责初始化一些基本的参数。
public function __construct( $meta_query = false ) { if ( is_array( $meta_query ) && isset( $meta_query['relation'] ) ) { $this->relation = strtoupper( $meta_query['relation'] ); if ( ! in_array( $this->relation, array( 'AND', 'OR' ), true ) ) { $this->relation = 'AND'; } } else { $this->relation = 'AND'; } if ( isset( $meta_query['queries'] ) ) { $meta_query = $meta_query['queries']; } $this->queries = $this->normalize_query( $meta_query ); }
$meta_query
:这是传入的查询参数,可以是一个数组,用来定义各种查询条件。$this->relation
:定义多个查询条件之间的关系,可以是 ‘AND’ (所有条件都满足) 或 ‘OR’ (满足任意一个条件)。 默认是 ‘AND’。$this->queries
:经过normalize_query()
函数处理后的标准化查询条件。
代码解读:
- 首先,它会检查
$meta_query
是否是一个数组,并且是否设置了relation
键。relation
决定了多个查询条件之间的逻辑关系。 - 如果
relation
的值不是 ‘AND’ 或 ‘OR’, 那么它会默认设置为 ‘AND’。 - 如果
$meta_query
中包含了queries
键, 那么它会将$meta_query
的值设置为queries
的值。 这样做是为了兼容一些旧的查询格式。 - 最后,它会调用
normalize_query()
函数来标准化查询条件。
-
normalize_query( $query )
:查询条件的标准化这个函数负责将传入的查询条件转换成统一的格式,方便后续的处理。
protected function normalize_query( $query ) { $normalized = array(); if ( ! is_array( $query ) ) { return $normalized; } foreach ( $query as $key => $value ) { if ( 'relation' === $key ) { continue; } if ( is_array( $value ) ) { $value = $this->normalize_query( $value ); if ( ! empty( $value ) ) { $normalized[] = $value; } } else { $normalized[] = $this->validate_query( $query ); break; } } return $normalized; }
代码解读:
- 这个函数会递归地处理查询条件,确保所有的条件都是一个数组。
- 如果查询条件中包含了
relation
键, 那么它会忽略这个键。 - 如果查询条件是一个数组, 那么它会递归地调用
normalize_query()
函数来处理这个数组。 - 如果查询条件不是一个数组, 那么它会调用
validate_query()
函数来验证这个条件。
-
validate_query( $query )
:查询条件的验证这个函数负责验证查询条件的合法性,确保没有错误的参数。
protected function validate_query( $query ) { $defaults = array( 'key' => '', 'value' => '', 'compare' => '=', 'type' => 'CHAR', ); $query = wp_parse_args( $query, $defaults ); $query['key'] = trim( $query['key'] ); $query['type'] = strtoupper( $query['type'] ); $query['compare'] = strtoupper( $query['compare'] ); if ( ! $query['key'] ) { return false; } if ( ! in_array( $query['type'], array( 'NUMERIC', 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'TIME' ), true ) ) { $query['type'] = 'CHAR'; } $valid_comparisons = array( '=', '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'REGEXP', 'NOT REGEXP', 'RLIKE' ); if ( ! in_array( $query['compare'], $valid_comparisons, true ) ) { $query['compare'] = '='; } return $query; }
代码解读:
- 这个函数首先定义了一个默认的查询条件数组
$defaults
。 - 然后,它使用
wp_parse_args()
函数将传入的查询条件和默认的查询条件合并起来。 - 接下来,它会对查询条件中的
key
,type
,compare
进行验证,确保它们的值是合法的。 - 如果
key
的值为空, 那么它会返回false
。 - 如果
type
的值不是NUMERIC
,BINARY
,CHAR
,DATE
,DATETIME
,TIME
中的一个, 那么它会将type
的值设置为CHAR
。 - 如果
compare
的值不是='
,!=
,>
,>=
,<
,<=
,LIKE
,NOT LIKE
,IN
,NOT IN
,BETWEEN
,NOT BETWEEN
,REGEXP
,NOT REGEXP
,RLIKE
中的一个, 那么它会将compare
的值设置为=
。 - 最后,它会返回验证后的查询条件。
- 这个函数首先定义了一个默认的查询条件数组
-
get_sql( $meta_table, $primary_table, $primary_id_column, $context = false )
:生成 SQL 查询语句这是最关键的函数,负责根据查询条件生成实际的 SQL 查询语句。
public function get_sql( $meta_table, $primary_table, $primary_id_column, $context = false ) { global $wpdb; $sql = $this->get_sql_clauses( $meta_table, $primary_table, $primary_id_column, $context ); $sql['where'] = 'AND ' . $sql['where']; return array( 'where' => $sql['where'], 'join' => $sql['join'], ); }
代码解读:
- 这个函数会调用
get_sql_clauses()
函数来获取 SQL 查询语句的各个部分, 包括where
子句和join
子句。 - 然后,它会将
where
子句和AND
关键字拼接起来。 - 最后,它会返回一个包含
where
子句和join
子句的数组。
- 这个函数会调用
-
get_sql_clauses( $meta_table, $primary_table, $primary_id_column, $context = false )
:构建 SQL 子句这个函数负责构建 SQL 查询语句的各个部分,包括
where
子句和join
子句。public function get_sql_clauses( $meta_table, $primary_table, $primary_id_column, $context = false ) { global $wpdb; $sql = array( 'where' => '1=1', 'join' => '', ); $clauses = $this->get_sql_for_query( $this->queries, $meta_table, $primary_table, $primary_id_column, $context ); if ( ! empty( $clauses['where'] ) ) { $sql['where'] = '(' . implode( " ) {$this->relation} ( ", $clauses['where'] ) . ')'; } if ( ! empty( $clauses['join'] ) ) { $sql['join'] = implode( ' ', $clauses['join'] ); } return $sql; }
代码解读:
- 这个函数首先初始化一个包含
where
子句和join
子句的数组$sql
。 - 然后,它会调用
get_sql_for_query()
函数来获取每个查询条件的 SQL 子句。 - 接下来,它会将每个查询条件的
where
子句用$this->relation
连接起来。 - 最后,它会将每个查询条件的
join
子句拼接起来。
- 这个函数首先初始化一个包含
-
get_sql_for_query( $query, $meta_table, $primary_table, $primary_id_column, $context = false )
:递归生成 SQL 子句这个函数负责递归地生成每个查询条件的 SQL 子句。
protected function get_sql_for_query( $query, $meta_table, $primary_table, $primary_id_column, $context = false ) { global $wpdb; $sql = array( 'where' => array(), 'join' => array(), ); foreach ( $query as $clause ) { if ( is_array( $clause ) ) { $clause_sql = $this->get_sql_for_query( $clause, $meta_table, $primary_table, $primary_id_column, $context ); $sql['where'] = array_merge( $sql['where'], $clause_sql['where'] ); $sql['join'] = array_merge( $sql['join'], $clause_sql['join'] ); } else { $where = $this->get_sql_for_clause( $clause, $meta_table, $primary_table, $primary_id_column, $context ); if ( ! empty( $where ) ) { $sql['where'][] = $where; } } } return $sql; }
代码解读:
- 这个函数会递归地处理查询条件,如果查询条件是一个数组, 那么它会递归地调用
get_sql_for_query()
函数来处理这个数组。 - 如果查询条件不是一个数组, 那么它会调用
get_sql_for_clause()
函数来获取这个查询条件的 SQL 子句。
- 这个函数会递归地处理查询条件,如果查询条件是一个数组, 那么它会递归地调用
-
get_sql_for_clause( $clause, $meta_table, $primary_table, $primary_id_column, $context = false )
:生成单个查询条件的 SQL 子句这个函数负责生成单个查询条件的 SQL 子句。
protected function get_sql_for_clause( $clause, $meta_table, $primary_table, $primary_id_column, $context = false ) { global $wpdb; $where = ''; $meta_key = esc_sql( $clause['key'] ); $meta_value = $clause['value']; $compare = $clause['compare']; $meta_type = $clause['type']; $column = 'meta_value'; switch ( $meta_type ) { case 'NUMERIC': $column = 'meta_value+0'; break; case 'BINARY': $column = 'BINARY meta_value'; break; } switch ( $compare ) { case 'IN': case 'NOT IN': if ( ! is_array( $meta_value ) ) { $meta_value = preg_split( '/[,s]+/', $meta_value ); } $meta_value = array_map( 'esc_sql', $meta_value ); $meta_value = "'" . implode( "', '", $meta_value ) . "'"; $where = "$meta_table.meta_key = '$meta_key' AND $column " . $compare . " ($meta_value)"; break; case 'BETWEEN': case 'NOT BETWEEN': if ( ! is_array( $meta_value ) || count( $meta_value ) != 2 ) { return ''; } $meta_value[0] = esc_sql( $meta_value[0] ); $meta_value[1] = esc_sql( $meta_value[1] ); $where = "$meta_table.meta_key = '$meta_key' AND $column " . $compare . " ('{$meta_value[0]}' AND '{$meta_value[1]}')"; break; case 'LIKE': case 'NOT LIKE': $meta_value = esc_sql( like_escape( $meta_value ) ); $where = "$meta_table.meta_key = '$meta_key' AND $column $compare '%$meta_value%'"; break; case 'REGEXP': case 'NOT REGEXP': case 'RLIKE': $meta_value = esc_sql( $meta_value ); $where = "$meta_table.meta_key = '$meta_key' AND $column $compare '$meta_value'"; break; default: $meta_value = esc_sql( $meta_value ); $where = "$meta_table.meta_key = '$meta_key' AND $column $compare '$meta_value'"; break; } return $where; }
代码解读:
- 这个函数会根据查询条件中的
key
,value
,compare
,type
来生成 SQL 子句。 - 它会使用
esc_sql()
函数来转义查询条件中的值,防止 SQL 注入。 - 它会根据
type
的值来选择不同的列名,例如NUMERIC
类型会使用meta_value+0
,BINARY
类型会使用BINARY meta_value
。 - 它会根据
compare
的值来生成不同的比较运算符,例如IN
,NOT IN
,BETWEEN
,NOT BETWEEN
,LIKE
,NOT LIKE
,REGEXP
,NOT REGEXP
,RLIKE
。
- 这个函数会根据查询条件中的
四、使用示例:查询分类术语元数据
现在,咱们来演示一下如何使用 WP_Term_Meta_Query
类来查询分类术语元数据。
<?php
// 1. 构建查询参数
$meta_query = array(
'relation' => 'AND', // 多个条件之间的关系:AND (默认), OR
array(
'key' => 'color', // 元数据键名
'value' => 'red', // 元数据值
'compare' => '=', // 比较运算符:=, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, BETWEEN, NOT BETWEEN, REGEXP, NOT REGEXP, RLIKE
'type' => 'CHAR', // 元数据类型:NUMERIC, BINARY, CHAR (默认), DATE, DATETIME, TIME
),
array(
'key' => 'size',
'value' => array( 10, 20 ),
'compare' => 'BETWEEN',
'type' => 'NUMERIC',
),
);
// 2. 创建 WP_Term_Meta_Query 对象
$meta_query_obj = new WP_Term_Meta_Query( $meta_query );
// 3. 获取 SQL 查询语句
$sql = $meta_query_obj->get_sql( 'wp_termmeta', 'wp_terms', 'term_id' );
// 4. 构建完整的 SQL 查询语句
global $wpdb;
$term_ids = $wpdb->get_col( "
SELECT tm.term_id
FROM wp_termmeta tm
{$sql['join']}
WHERE 1=1 {$sql['where']}
" );
// 5. 处理查询结果
if ( ! empty( $term_ids ) ) {
echo "符合条件的分类术语 ID:<br>";
foreach ( $term_ids as $term_id ) {
echo $term_id . "<br>";
}
} else {
echo "没有找到符合条件的分类术语。";
}
?>
代码解读:
- 首先,我们构建了一个
$meta_query
数组,定义了两个查询条件。- 第一个条件是
color
元数据的值等于red
。 - 第二个条件是
size
元数据的值在10
和20
之间。
- 第一个条件是
- 然后,我们创建了一个
WP_Term_Meta_Query
对象,并将$meta_query
数组作为参数传递给它。 - 接下来,我们调用
get_sql()
函数来获取 SQL 查询语句。 - 最后,我们使用
$wpdb->get_col()
函数来执行 SQL 查询,并将查询结果输出到页面上。
五、WP_Term_Meta_Query
的优点
- 灵活性: 允许你使用各种查询条件来过滤分类术语元数据。
- 安全性: 自动转义查询条件中的值,防止 SQL 注入。
- 可扩展性: 可以与其他 WordPress API 结合使用,实现更复杂的功能。
六、WP_Term_Meta_Query
的局限性
- 性能: 对于大型数据集,复杂的查询可能会影响性能。
- 学习曲线: 需要一定的 SQL 知识才能灵活使用。
七、总结
WP_Term_Meta_Query
类是 WordPress 中一个强大的工具,可以让你轻松地查询分类术语元数据。 掌握它的使用方法,可以让你更好地管理你的 WordPress 网站。
表格总结:
函数名 | 功能 |
---|---|
__construct() |
初始化查询参数,设置查询条件之间的关系。 |
normalize_query() |
将查询条件转换为统一的格式,方便后续处理。 |
validate_query() |
验证查询条件的合法性,确保没有错误的参数。 |
get_sql() |
生成 SQL 查询语句,返回包含 where 子句和 join 子句的数组。 |
get_sql_clauses() |
构建 SQL 查询语句的各个部分,包括 where 子句和 join 子句。 |
get_sql_for_query() |
递归地生成每个查询条件的 SQL 子句。 |
get_sql_for_clause() |
生成单个查询条件的 SQL 子句,包括 meta_key , meta_value , compare , meta_type 等。 |
好了,今天的讲座就到这里。希望大家以后看到 WP_Term_Meta_Query
这位老朋友,不会再感到陌生和恐惧了! 咱们下次再见!