diff --git a/wp-includes/classes.php b/wp-includes/classes.php index 7290b11317..27fb326204 100644 --- a/wp-includes/classes.php +++ b/wp-includes/classes.php @@ -535,7 +535,17 @@ class WP { 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 * @access public @@ -543,6 +553,27 @@ class WP_Object_Query { */ 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 * @@ -573,6 +604,8 @@ class WP_Object_Query { * @access protected * @since 3.1.0 * + * @uses $this->meta_query + * * @param string $primary_table * @param string $primary_id_column * @param string $meta_table @@ -645,6 +678,44 @@ class WP_Object_Query { 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 * diff --git a/wp-includes/query.php b/wp-includes/query.php index a4547254d6..a78316bc9f 100644 --- a/wp-includes/query.php +++ b/wp-includes/query.php @@ -661,15 +661,6 @@ class WP_Query extends WP_Object_Query { */ 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. * @@ -1382,6 +1373,8 @@ class WP_Query extends WP_Object_Query { $this->is_tax = true; } + $this->parse_tax_query( $qv ); + $this->parse_meta_query( $qv ); 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)); } + 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. * @@ -1815,118 +1921,7 @@ class WP_Query extends WP_Object_Query { $search = apply_filters_ref_array('posts_search', array( $search, &$this ) ); // Taxonomies - $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' - ); - } - - if ( !empty( $tax_query ) ) { - $this->tax_query = $tax_query; - + if ( !empty( $this->tax_query ) ) { if ( empty($post_type) ) { $post_type = 'any'; $post_status_join = true; @@ -1934,15 +1929,11 @@ class WP_Query extends WP_Object_Query { $post_status_join = true; } - $ids = wp_tax_query( $tax_query ); - if ( !empty($ids) ) - $where .= " AND $wpdb->posts.ID IN(" . implode( ', ', $ids ) . ")"; - else - $where .= ' AND 0 = 1'; + $where .= $this->get_tax_sql( "$wpdb->posts.ID" ); // Back-compat 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 ) ) { $cat_query = reset( $cat_query ); $cat = get_term_by( $cat_query['field'], $cat_query['terms'][0], 'category' ); diff --git a/wp-includes/taxonomy.php b/wp-includes/taxonomy.php index 9b1487172c..9e455c27b6 100644 --- a/wp-includes/taxonomy.php +++ b/wp-includes/taxonomy.php @@ -532,40 +532,6 @@ function get_objects_in_term( $terms, $taxonomies, $args = array() ) { 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. *