Generalize taxonomy queries:
* transform wp_tax_query() into WP_Object_Query::get_tax_sql() * create parse_tax_query() method in WP_Query * add doc-block for $tax_query and $meta_query See #15032. See #12891. git-svn-id: http://svn.automattic.com/wordpress/trunk@15731 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
ef709f8953
commit
a8367fa6f2
|
@ -535,7 +535,17 @@ class WP {
|
||||||
class WP_Object_Query {
|
class WP_Object_Query {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata query
|
* List of metadata queries
|
||||||
|
*
|
||||||
|
* A query is an associative array:
|
||||||
|
* - 'key' string The meta key
|
||||||
|
* - 'value' string|array The meta value
|
||||||
|
* - 'compare' (optional) string How to compare the key to the value.
|
||||||
|
* Possible values: '=', '!=', '>', '>=', '<', '<=', 'LIKE', 'IN', 'BETWEEN'.
|
||||||
|
* Default: '='
|
||||||
|
* - 'type' string (optional) The type of the value.
|
||||||
|
* Possible values: 'NUMERIC', 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', 'UNSIGNED'.
|
||||||
|
* Default: 'CHAR'
|
||||||
*
|
*
|
||||||
* @since 3.1.0
|
* @since 3.1.0
|
||||||
* @access public
|
* @access public
|
||||||
|
@ -543,6 +553,27 @@ class WP_Object_Query {
|
||||||
*/
|
*/
|
||||||
var $meta_query = array();
|
var $meta_query = array();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List of taxonomy queries
|
||||||
|
*
|
||||||
|
* A query is an associative array:
|
||||||
|
* - 'taxonomy' string|array The taxonomy being queried
|
||||||
|
* - 'terms' string|array The list of terms
|
||||||
|
* - 'field' string (optional) Which term field is being used.
|
||||||
|
* Possible values: 'term_id', 'slug' or 'name'
|
||||||
|
* Default: 'slug'
|
||||||
|
* - 'operator' string (optional)
|
||||||
|
* Possible values: 'IN' and 'NOT IN'.
|
||||||
|
* Default: 'IN'
|
||||||
|
* - 'include_children' bool (optional) Wether to include child terms.
|
||||||
|
* Default: true
|
||||||
|
*
|
||||||
|
* @since 3.1.0
|
||||||
|
* @access public
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
var $tax_query = array();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Populates the $meta_query property
|
* Populates the $meta_query property
|
||||||
*
|
*
|
||||||
|
@ -573,6 +604,8 @@ class WP_Object_Query {
|
||||||
* @access protected
|
* @access protected
|
||||||
* @since 3.1.0
|
* @since 3.1.0
|
||||||
*
|
*
|
||||||
|
* @uses $this->meta_query
|
||||||
|
*
|
||||||
* @param string $primary_table
|
* @param string $primary_table
|
||||||
* @param string $primary_id_column
|
* @param string $primary_id_column
|
||||||
* @param string $meta_table
|
* @param string $meta_table
|
||||||
|
@ -645,6 +678,44 @@ class WP_Object_Query {
|
||||||
return array( $join, $where );
|
return array( $join, $where );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used internally to generate an SQL string for searching across multiple taxonomies
|
||||||
|
*
|
||||||
|
* @access protected
|
||||||
|
* @since 3.1.0
|
||||||
|
*
|
||||||
|
* @uses $this->tax_query
|
||||||
|
*
|
||||||
|
* @param string $object_id_column
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function get_tax_sql( $object_id_column ) {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$sql = array();
|
||||||
|
foreach ( $this->tax_query as $query ) {
|
||||||
|
if ( !isset( $query['include_children'] ) )
|
||||||
|
$query['include_children'] = true;
|
||||||
|
$query['do_query'] = false;
|
||||||
|
$sql[] = get_objects_in_term( $query['terms'], $query['taxonomy'], $query );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 1 == count( $sql ) ) {
|
||||||
|
$ids = $wpdb->get_col( $sql[0] );
|
||||||
|
} else {
|
||||||
|
$r = "SELECT object_id FROM $wpdb->term_relationships WHERE 1=1";
|
||||||
|
foreach ( $sql as $query )
|
||||||
|
$r .= " AND object_id IN ($query)";
|
||||||
|
|
||||||
|
$ids = $wpdb->get_col( $r );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !empty( $ids ) )
|
||||||
|
return " AND $object_id_column IN(" . implode( ', ', $ids ) . ")";
|
||||||
|
else
|
||||||
|
return ' AND 0 = 1';
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used internally to generate an SQL string for searching across multiple columns
|
* Used internally to generate an SQL string for searching across multiple columns
|
||||||
*
|
*
|
||||||
|
|
|
@ -661,15 +661,6 @@ class WP_Query extends WP_Object_Query {
|
||||||
*/
|
*/
|
||||||
var $query_vars = array();
|
var $query_vars = array();
|
||||||
|
|
||||||
/**
|
|
||||||
* Taxonomy query, after parsing
|
|
||||||
*
|
|
||||||
* @since 3.1.0
|
|
||||||
* @access public
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
var $tax_query = array();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the data for a single object that is queried.
|
* Holds the data for a single object that is queried.
|
||||||
*
|
*
|
||||||
|
@ -1382,6 +1373,8 @@ class WP_Query extends WP_Object_Query {
|
||||||
$this->is_tax = true;
|
$this->is_tax = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->parse_tax_query( $qv );
|
||||||
|
|
||||||
$this->parse_meta_query( $qv );
|
$this->parse_meta_query( $qv );
|
||||||
|
|
||||||
if ( empty($qv['author']) || ($qv['author'] == '0') ) {
|
if ( empty($qv['author']) || ($qv['author'] == '0') ) {
|
||||||
|
@ -1490,6 +1483,119 @@ class WP_Query extends WP_Object_Query {
|
||||||
do_action_ref_array('parse_query', array(&$this));
|
do_action_ref_array('parse_query', array(&$this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parse_tax_query( $q ) {
|
||||||
|
$tax_query = array();
|
||||||
|
|
||||||
|
if ( $this->is_tax ) {
|
||||||
|
foreach ( $GLOBALS['wp_taxonomies'] as $taxonomy => $t ) {
|
||||||
|
if ( $t->query_var && !empty( $q[$t->query_var] ) ) {
|
||||||
|
$tax_query_defaults = array(
|
||||||
|
'taxonomy' => $taxonomy,
|
||||||
|
'field' => 'slug',
|
||||||
|
'operator' => 'IN'
|
||||||
|
);
|
||||||
|
|
||||||
|
$term = str_replace( ' ', '+', $q[$t->query_var] );
|
||||||
|
|
||||||
|
if ( strpos($term, '+') !== false ) {
|
||||||
|
$terms = preg_split( '/[+\s]+/', $term );
|
||||||
|
foreach ( $terms as $term ) {
|
||||||
|
$tax_query[] = array_merge( $tax_query_defaults, array(
|
||||||
|
'terms' => array( $term )
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$tax_query[] = array_merge( $tax_query_defaults, array(
|
||||||
|
'terms' => preg_split('/[,\s]+/', $term)
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category stuff
|
||||||
|
if ( !empty($q['cat']) && '0' != $q['cat'] && !$this->is_singular ) {
|
||||||
|
$q['cat'] = ''.urldecode($q['cat']).'';
|
||||||
|
$q['cat'] = addslashes_gpc($q['cat']);
|
||||||
|
$cat_array = preg_split('/[,\s]+/', $q['cat']);
|
||||||
|
$q['cat'] = '';
|
||||||
|
$req_cats = array();
|
||||||
|
foreach ( (array) $cat_array as $cat ) {
|
||||||
|
$cat = intval($cat);
|
||||||
|
$req_cats[] = $cat;
|
||||||
|
$in = ($cat > 0);
|
||||||
|
$cat = abs($cat);
|
||||||
|
if ( $in ) {
|
||||||
|
$q['category__in'][] = $cat;
|
||||||
|
} else {
|
||||||
|
$q['category__not_in'][] = $cat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$q['cat'] = implode(',', $req_cats);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !empty($q['category__in']) ) {
|
||||||
|
$tax_query[] = array(
|
||||||
|
'taxonomy' => 'category',
|
||||||
|
'terms' => $q['category__in'],
|
||||||
|
'operator' => 'IN',
|
||||||
|
'field' => 'term_id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !empty($q['category__not_in']) ) {
|
||||||
|
$tax_query[] = array(
|
||||||
|
'taxonomy' => 'category',
|
||||||
|
'terms' => $q['category__not_in'],
|
||||||
|
'operator' => 'NOT IN',
|
||||||
|
'field' => 'term_id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category stuff for nice URLs
|
||||||
|
if ( '' != $q['category_name'] && !$this->is_singular ) {
|
||||||
|
$q['category_name'] = str_replace( '%2F', '/', urlencode(urldecode($q['category_name'])) );
|
||||||
|
$q['category_name'] = '/' . trim( $q['category_name'], '/' );
|
||||||
|
|
||||||
|
$tax_query[] = array(
|
||||||
|
'taxonomy' => 'category',
|
||||||
|
'terms' => array( basename( $q['category_name'] ) ),
|
||||||
|
'operator' => 'IN',
|
||||||
|
'field' => 'slug'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag stuff
|
||||||
|
if ( !empty($qv['tag_id']) ) {
|
||||||
|
$tax_query[] = array(
|
||||||
|
'taxonomy' => 'post_tag',
|
||||||
|
'terms' => $qv['tag_id'],
|
||||||
|
'operator' => 'IN',
|
||||||
|
'field' => 'term_id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !empty($q['tag__in']) ) {
|
||||||
|
$tax_query[] = array(
|
||||||
|
'taxonomy' => 'post_tag',
|
||||||
|
'terms' => $q['tag__in'],
|
||||||
|
'operator' => 'IN',
|
||||||
|
'field' => 'term_id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !empty($q['tag__not_in']) ) {
|
||||||
|
$tax_query[] = array(
|
||||||
|
'taxonomy' => 'post_tag',
|
||||||
|
'terms' => $q['tag__not_in'],
|
||||||
|
'operator' => 'NOT IN',
|
||||||
|
'field' => 'term_id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tax_query = $tax_query;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the 404 property and saves whether query is feed.
|
* Sets the 404 property and saves whether query is feed.
|
||||||
*
|
*
|
||||||
|
@ -1815,118 +1921,7 @@ class WP_Query extends WP_Object_Query {
|
||||||
$search = apply_filters_ref_array('posts_search', array( $search, &$this ) );
|
$search = apply_filters_ref_array('posts_search', array( $search, &$this ) );
|
||||||
|
|
||||||
// Taxonomies
|
// Taxonomies
|
||||||
$tax_query = array();
|
if ( !empty( $this->tax_query ) ) {
|
||||||
|
|
||||||
if ( $this->is_tax ) {
|
|
||||||
foreach ( $GLOBALS['wp_taxonomies'] as $taxonomy => $t ) {
|
|
||||||
if ( $t->query_var && !empty( $q[$t->query_var] ) ) {
|
|
||||||
$tax_query_defaults = array(
|
|
||||||
'taxonomy' => $taxonomy,
|
|
||||||
'field' => 'slug',
|
|
||||||
'operator' => 'IN'
|
|
||||||
);
|
|
||||||
|
|
||||||
$term = str_replace( ' ', '+', $q[$t->query_var] );
|
|
||||||
|
|
||||||
if ( strpos($term, '+') !== false ) {
|
|
||||||
$terms = preg_split( '/[+\s]+/', $term );
|
|
||||||
foreach ( $terms as $term ) {
|
|
||||||
$tax_query[] = array_merge( $tax_query_defaults, array(
|
|
||||||
'terms' => array( $term )
|
|
||||||
) );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$tax_query[] = array_merge( $tax_query_defaults, array(
|
|
||||||
'terms' => preg_split('/[,\s]+/', $term)
|
|
||||||
) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category stuff
|
|
||||||
if ( !empty($q['cat']) && '0' != $q['cat'] && !$this->is_singular ) {
|
|
||||||
$q['cat'] = ''.urldecode($q['cat']).'';
|
|
||||||
$q['cat'] = addslashes_gpc($q['cat']);
|
|
||||||
$cat_array = preg_split('/[,\s]+/', $q['cat']);
|
|
||||||
$q['cat'] = '';
|
|
||||||
$req_cats = array();
|
|
||||||
foreach ( (array) $cat_array as $cat ) {
|
|
||||||
$cat = intval($cat);
|
|
||||||
$req_cats[] = $cat;
|
|
||||||
$in = ($cat > 0);
|
|
||||||
$cat = abs($cat);
|
|
||||||
if ( $in ) {
|
|
||||||
$q['category__in'][] = $cat;
|
|
||||||
} else {
|
|
||||||
$q['category__not_in'][] = $cat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$q['cat'] = implode(',', $req_cats);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !empty($q['category__in']) ) {
|
|
||||||
$tax_query[] = array(
|
|
||||||
'taxonomy' => 'category',
|
|
||||||
'terms' => $q['category__in'],
|
|
||||||
'operator' => 'IN',
|
|
||||||
'field' => 'term_id'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !empty($q['category__not_in']) ) {
|
|
||||||
$tax_query[] = array(
|
|
||||||
'taxonomy' => 'category',
|
|
||||||
'terms' => $q['category__not_in'],
|
|
||||||
'operator' => 'NOT IN',
|
|
||||||
'field' => 'term_id'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Category stuff for nice URLs
|
|
||||||
if ( '' != $q['category_name'] && !$this->is_singular ) {
|
|
||||||
$q['category_name'] = str_replace( '%2F', '/', urlencode(urldecode($q['category_name'])) );
|
|
||||||
$q['category_name'] = '/' . trim( $q['category_name'], '/' );
|
|
||||||
|
|
||||||
$tax_query[] = array(
|
|
||||||
'taxonomy' => 'category',
|
|
||||||
'terms' => array( basename( $q['category_name'] ) ),
|
|
||||||
'operator' => 'IN',
|
|
||||||
'field' => 'slug'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tag stuff
|
|
||||||
if ( !empty($qv['tag_id']) ) {
|
|
||||||
$tax_query[] = array(
|
|
||||||
'taxonomy' => 'post_tag',
|
|
||||||
'terms' => $qv['tag_id'],
|
|
||||||
'operator' => 'IN',
|
|
||||||
'field' => 'term_id'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !empty($q['tag__in']) ) {
|
|
||||||
$tax_query[] = array(
|
|
||||||
'taxonomy' => 'post_tag',
|
|
||||||
'terms' => $q['tag__in'],
|
|
||||||
'operator' => 'IN',
|
|
||||||
'field' => 'term_id'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !empty($q['tag__not_in']) ) {
|
|
||||||
$tax_query[] = array(
|
|
||||||
'taxonomy' => 'post_tag',
|
|
||||||
'terms' => $q['tag__not_in'],
|
|
||||||
'operator' => 'NOT IN',
|
|
||||||
'field' => 'term_id'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !empty( $tax_query ) ) {
|
|
||||||
$this->tax_query = $tax_query;
|
|
||||||
|
|
||||||
if ( empty($post_type) ) {
|
if ( empty($post_type) ) {
|
||||||
$post_type = 'any';
|
$post_type = 'any';
|
||||||
$post_status_join = true;
|
$post_status_join = true;
|
||||||
|
@ -1934,15 +1929,11 @@ class WP_Query extends WP_Object_Query {
|
||||||
$post_status_join = true;
|
$post_status_join = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ids = wp_tax_query( $tax_query );
|
$where .= $this->get_tax_sql( "$wpdb->posts.ID" );
|
||||||
if ( !empty($ids) )
|
|
||||||
$where .= " AND $wpdb->posts.ID IN(" . implode( ', ', $ids ) . ")";
|
|
||||||
else
|
|
||||||
$where .= ' AND 0 = 1';
|
|
||||||
|
|
||||||
// Back-compat
|
// Back-compat
|
||||||
if ( !empty( $ids ) ) {
|
if ( !empty( $ids ) ) {
|
||||||
$cat_query = wp_list_filter( $tax_query, array( 'taxonomy' => 'category' ) );
|
$cat_query = wp_list_filter( $this->tax_query, array( 'taxonomy' => 'category' ) );
|
||||||
if ( !empty( $cat_query ) ) {
|
if ( !empty( $cat_query ) ) {
|
||||||
$cat_query = reset( $cat_query );
|
$cat_query = reset( $cat_query );
|
||||||
$cat = get_term_by( $cat_query['field'], $cat_query['terms'][0], 'category' );
|
$cat = get_term_by( $cat_query['field'], $cat_query['terms'][0], 'category' );
|
||||||
|
|
|
@ -532,40 +532,6 @@ function get_objects_in_term( $terms, $taxonomies, $args = array() ) {
|
||||||
return $wpdb->get_col( $sql );
|
return $wpdb->get_col( $sql );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Retrieve object_ids matching one or more taxonomy queries
|
|
||||||
*
|
|
||||||
* @since 3.1.0
|
|
||||||
*
|
|
||||||
* @param array $queries A list of taxonomy queries. A query is an associative array:
|
|
||||||
* 'taxonomy' string|array The taxonomy being queried
|
|
||||||
* 'terms' string|array The list of terms
|
|
||||||
* 'field' string Which term field is being used. Can be 'term_id', 'slug' or 'name'
|
|
||||||
* 'operator' string Can be 'IN' and 'NOT IN'
|
|
||||||
*
|
|
||||||
* @return array|WP_Error List of matching object_ids; WP_Error on failure.
|
|
||||||
*/
|
|
||||||
function wp_tax_query( $queries ) {
|
|
||||||
global $wpdb;
|
|
||||||
|
|
||||||
$sql = array();
|
|
||||||
foreach ( $queries as $query ) {
|
|
||||||
if ( !isset( $query['include_children'] ) )
|
|
||||||
$query['include_children'] = true;
|
|
||||||
$query['do_query'] = false;
|
|
||||||
$sql[] = get_objects_in_term( $query['terms'], $query['taxonomy'], $query );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( 1 == count( $sql ) )
|
|
||||||
return $wpdb->get_col( $sql[0] );
|
|
||||||
|
|
||||||
$r = "SELECT object_id FROM $wpdb->term_relationships WHERE 1=1";
|
|
||||||
foreach ( $sql as $query )
|
|
||||||
$r .= " AND object_id IN ($query)";
|
|
||||||
|
|
||||||
return $wpdb->get_col( $r );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all Term data from database by Term ID.
|
* Get all Term data from database by Term ID.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue