Make WordPress Core

Changeset 29891


Ignore:
Timestamp:
10/14/2014 04:02:41 AM (10 years ago)
Author:
boonebgorges
Message:

Introduce support for nested queries in WP_Tax_Query.

Previously, tax query arguments could be joined by a single AND or OR relation.
Now, these queries can be arbitrarily nested, allowing clauses to be linked
together with multiple relations.

In a few places, WP_Query runs through a list of clauses in a tax_query in order
to set certain query vars for backward compatibility. The necessary changes have
been made to WP_Query to support this feature with the new complex structure of
tax_query. Unit tests are included for these backward compatibility fixes.

Unit tests for the new nesting syntax are included.

Props boonebgorges.
Fixes #29718. See #29738.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/query.php

    r29855 r29891  
    16731673
    16741674            foreach ( $this->tax_query->queries as $tax_query ) {
    1675                 if ( 'NOT IN' != $tax_query['operator'] ) {
     1675                if ( ! is_array( $tax_query ) ) {
     1676                    continue;
     1677                }
     1678
     1679                if ( isset( $tax_query['operator'] ) && 'NOT IN' != $tax_query['operator'] ) {
    16761680                    switch ( $tax_query['taxonomy'] ) {
    16771681                        case 'category':
     
    26882692                // Do a fully inclusive search for currently registered post types of queried taxonomies
    26892693                $post_type = array();
    2690                 $taxonomies = wp_list_pluck( $this->tax_query->queries, 'taxonomy' );
     2694                $taxonomies = array_keys( $this->tax_query->queried_terms );
    26912695                foreach ( get_post_types( array( 'exclude_from_search' => false ) ) as $pt ) {
    26922696                    $object_taxonomies = $pt === 'attachment' ? get_taxonomies_for_attachments() : get_object_taxonomies( $pt );
     
    27052709        }
    27062710
    2707         // Back-compat
    2708         if ( !empty($this->tax_query->queries) ) {
    2709             $tax_query_in_and = wp_list_filter( $this->tax_query->queries, array( 'operator' => 'NOT IN' ), 'NOT' );
    2710             if ( !empty( $tax_query_in_and ) ) {
    2711                 if ( !isset( $q['taxonomy'] ) ) {
    2712                     foreach ( $tax_query_in_and as $a_tax_query ) {
    2713                         if ( !in_array( $a_tax_query['taxonomy'], array( 'category', 'post_tag' ) ) ) {
    2714                             $q['taxonomy'] = $a_tax_query['taxonomy'];
    2715                             if ( 'slug' == $a_tax_query['field'] )
    2716                                 $q['term'] = $a_tax_query['terms'][0];
    2717                             else
    2718                                 $q['term_id'] = $a_tax_query['terms'][0];
    2719 
    2720                             break;
     2711        /*
     2712         * Ensure that 'taxonomy', 'term', 'term_id', 'cat', and
     2713         * 'category_name' vars are set for backward compatibility.
     2714         */
     2715        if ( ! empty( $this->tax_query->queried_terms ) ) {
     2716
     2717            /*
     2718             * Set 'taxonomy', 'term', and 'term_id' to the
     2719             * first taxonomy other than 'post_tag' or 'category'.
     2720             */
     2721            if ( ! isset( $q['taxonomy'] ) ) {
     2722                foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
     2723                    if ( empty( $queried_items['terms'][0] ) ) {
     2724                        continue;
     2725                    }
     2726
     2727                    if ( ! in_array( $queried_taxonomy, array( 'category', 'post_tag' ) ) ) {
     2728                        $q['taxonomy'] = $queried_taxonomy;
     2729
     2730                        if ( 'slug' === $queried_items['field'] ) {
     2731                            $q['term'] = $queried_items['terms'][0];
     2732                        } else {
     2733                            $q['term_id'] = $queried_items['terms'][0];
    27212734                        }
    27222735                    }
    27232736                }
    2724 
    2725                 $cat_query = wp_list_filter( $tax_query_in_and, array( 'taxonomy' => 'category' ) );
    2726                 if ( ! empty( $cat_query ) ) {
    2727                     $cat_query = reset( $cat_query );
    2728 
    2729                     if ( ! empty( $cat_query['terms'][0] ) ) {
    2730                         $the_cat = get_term_by( $cat_query['field'], $cat_query['terms'][0], 'category' );
    2731                         if ( $the_cat ) {
    2732                             $this->set( 'cat', $the_cat->term_id );
    2733                             $this->set( 'category_name', $the_cat->slug );
    2734                         }
    2735                         unset( $the_cat );
     2737            }
     2738
     2739            // 'cat', 'category_name', 'tag_id'
     2740            foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
     2741                if ( empty( $queried_items['terms'][0] ) ) {
     2742                    continue;
     2743                }
     2744
     2745                if ( 'category' === $queried_taxonomy ) {
     2746                    $the_cat = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'category' );
     2747                    if ( $the_cat ) {
     2748                        $this->set( 'cat', $the_cat->term_id );
     2749                        $this->set( 'category_name', $the_cat->slug );
    27362750                    }
     2751                    unset( $the_cat );
    27372752                }
    2738                 unset( $cat_query );
    2739 
    2740                 $tag_query = wp_list_filter( $tax_query_in_and, array( 'taxonomy' => 'post_tag' ) );
    2741                 if ( ! empty( $tag_query ) ) {
    2742                     $tag_query = reset( $tag_query );
    2743 
    2744                     if ( ! empty( $tag_query['terms'][0] ) ) {
    2745                         $the_tag = get_term_by( $tag_query['field'], $tag_query['terms'][0], 'post_tag' );
    2746                         if ( $the_tag )
    2747                             $this->set( 'tag_id', $the_tag->term_id );
    2748                         unset( $the_tag );
     2753
     2754                if ( 'post_tag' === $queried_taxonomy ) {
     2755                    $the_tag = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'post_tag' );
     2756                    if ( $the_tag ) {
     2757                        $this->set( 'tag_id', $the_tag->term_id );
    27492758                    }
     2759                    unset( $the_tag );
    27502760                }
    2751                 unset( $tag_query );
    27522761            }
    27532762        }
  • trunk/src/wp-includes/taxonomy.php

    r29867 r29891  
    628628
    629629/**
    630  * Container class for a multiple taxonomy query.
     630 * Class for generating SQL clauses that filter a primary query according to object taxonomy terms.
     631 *
     632 * `WP_Tax_Query` is a helper that allows primary query classes, such as {@see WP_Query}, to filter
     633 * their results by object metadata, by generating `JOIN` and `WHERE` subclauses to be attached
     634 * to the primary SQL query string.
    631635 *
    632636 * @since 3.1.0
     
    635639
    636640    /**
    637      * List of taxonomy queries. A single taxonomy query is an associative array:
    638      * - 'taxonomy' string The taxonomy being queried. Optional when using the term_taxonomy_id field.
    639      * - 'terms' string|array The list of terms
    640      * - 'field' string (optional) Which term field is being used.
    641      *      Possible values: 'term_id', 'slug', 'name', or 'term_taxonomy_id'
    642      *      Default: 'term_id'
    643      * - 'operator' string (optional)
    644      *      Possible values: 'AND', 'IN' or 'NOT IN'.
    645      *      Default: 'IN'
    646      * - 'include_children' bool (optional) Whether to include child terms. Requires that a taxonomy be specified.
    647      *      Default: true
     641     * Array of taxonomy queries.
     642     *
     643     * See {@see WP_Tax_Query::__construct()} for information on tax query arguments.
    648644     *
    649645     * @since 3.1.0
     
    669665     * @var string
    670666     */
    671     private static $no_results = array( 'join' => '', 'where' => ' AND 0 = 1' );
     667    private static $no_results = array( 'join' => array( '' ), 'where' => array( '0 = 1' ) );
     668
     669    /**
     670     * A flat list of table aliases used in the JOIN clauses.
     671     *
     672     * @since 4.1.0
     673     * @access protected
     674     * @var array
     675     */
     676    protected $table_aliases = array();
     677
     678    /**
     679     * Terms and taxonomies fetched by this query.
     680     *
     681     * We store this data in a flat array because they are referenced in a
     682     * number of places by WP_Query.
     683     *
     684     * @since 4.1.0
     685     * @access public
     686     * @var array
     687     */
     688    public $queried_terms = array();
    672689
    673690    /**
    674691     * Constructor.
    675      *
    676      * Parses a compact tax query and sets defaults.
    677692     *
    678693     * @since 3.1.0
    679694     * @access public
    680695     *
    681      * @param array $tax_query A compact tax query:
    682      *  array(
    683      *    'relation' => 'OR',
    684      *    array(
    685      *      'taxonomy' => 'tax1',
    686      *      'terms' => array( 'term1', 'term2' ),
    687      *      'field' => 'slug',
    688      *    ),
    689      *    array(
    690      *      'taxonomy' => 'tax2',
    691      *      'terms' => array( 'term-a', 'term-b' ),
    692      *      'field' => 'slug',
    693      *    ),
    694      *  )
     696     * @param array $tax_query {
     697     *     Array of taxonoy query clauses.
     698     *
     699     *     @type string $relation Optional. The MySQL keyword used to join
     700     *                            the clauses of the query. Accepts 'AND', or 'OR'. Default 'AND'.
     701     *     @type array {
     702     *         Optional. An array of first-order clause parameters, or another fully-formed tax query.
     703     *
     704     *         @type string           $taxonomy         Taxonomy being queried. Optional when field=term_taxonomy_id.
     705     *         @type string|int|array $terms            Term or terms to filter by.
     706     *         @type string           $field            Field to match $terms against. Accepts 'term_id', 'slug',
     707     *                                                 'name', or 'term_taxonomy_id'. Default: 'term_id'.
     708     *         @type string           $operator         MySQL operator to be used with $terms in the WHERE clause.
     709     *                                                  Accepts 'AND', 'IN', or 'OR. Default: 'IN'.
     710     *         @type bool             $include_children Optional. Whether to include child terms.
     711     *                                                  Requires a $taxonomy. Default: true.
     712     *     }
     713     * }
    695714     */
    696715    public function __construct( $tax_query ) {
     
    701720        }
    702721
     722        $this->queries = $this->sanitize_query( $tax_query );
     723    }
     724
     725    /**
     726     * Ensure the `tax_query` argument passed to the class constructor is well-formed.
     727     *
     728     * Ensures that each query-level clause has a 'relation' key, and that
     729     * each first-order clause contains all the necessary keys from $defaults.
     730     *
     731     * @since 4.1.0
     732     * @access public
     733     *
     734     * @param  array $queries Array of queries clauses.
     735     * @return array Sanitized array of query clauses.
     736     */
     737    public function sanitize_query( $queries ) {
     738        $cleaned_query = array();
     739
    703740        $defaults = array(
    704741            'taxonomy' => '',
    705742            'terms' => array(),
    706             'include_children' => true,
    707743            'field' => 'term_id',
    708744            'operator' => 'IN',
     745            'include_children' => true,
    709746        );
    710747
    711         foreach ( $tax_query as $query ) {
    712             if ( ! is_array( $query ) )
    713                 continue;
    714 
    715             $query = array_merge( $defaults, $query );
    716 
    717             $query['terms'] = (array) $query['terms'];
    718 
    719             $this->queries[] = $query;
    720         }
     748        foreach ( $queries as $key => $query ) {
     749            if ( 'relation' === $key ) {
     750                $cleaned_query['relation'] = $query;
     751
     752            // First-order clause.
     753            } else if ( self::is_first_order_clause( $query ) ) {
     754
     755                $cleaned_clause = array_merge( $defaults, $query );
     756                $cleaned_clause['terms'] = (array) $cleaned_clause['terms'];
     757                $cleaned_query[] = $cleaned_clause;
     758
     759                /*
     760                 * Keep a copy of the clause in the flate
     761                 * $queried_terms array, for use in WP_Query.
     762                 */
     763                if ( ! empty( $cleaned_clause['taxonomy'] ) && 'NOT IN' !== $cleaned_clause['operator'] ) {
     764                    $taxonomy = $cleaned_clause['taxonomy'];
     765                    if ( ! isset( $this->queried_terms[ $taxonomy ] ) ) {
     766                        $this->queried_terms[ $taxonomy ] = array();
     767                    }
     768
     769                    /*
     770                     * Backward compatibility: Only store the first
     771                     * 'terms' and 'field' found for a given taxonomy.
     772                     */
     773                    if ( ! empty( $cleaned_clause['terms'] ) && ! isset( $this->queried_terms[ $taxonomy ]['terms'] ) ) {
     774                        $this->queried_terms[ $taxonomy ]['terms'] = $cleaned_clause['terms'];
     775                    }
     776
     777                    if ( ! empty( $cleaned_clause['field'] ) && ! isset( $this->queried_terms[ $taxonomy ]['field'] ) ) {
     778                        $this->queried_terms[ $taxonomy ]['field'] = $cleaned_clause['field'];
     779                    }
     780                }
     781
     782            // Otherwise, it's a nested query, so we recurse.
     783            } else if ( is_array( $query ) ) {
     784                $cleaned_subquery = $this->sanitize_query( $query );
     785
     786                if ( ! empty( $cleaned_subquery ) ) {
     787                    $cleaned_query[] = $cleaned_subquery;
     788                }
     789            }
     790        }
     791
     792        return $cleaned_query;
     793    }
     794
     795    /**
     796     * Determine whether a clause is first-order.
     797     *
     798     * A "first-order" clause is one that contains any of the first-order
     799     * clause keys ('terms', 'taxonomy', 'include_children', 'field',
     800     * 'operator'). An empty clause also counts as a first-order clause,
     801     * for backward compatibility. Any clause that doesn't meet this is
     802     * determined, by process of elimination, to be a higher-order query.
     803     *
     804     * @since 4.1.0
     805     * @access protected
     806     *
     807     * @param  array $query Tax query arguments.
     808     * @return bool  Whether the query clause is a first-order clause.
     809     */
     810    protected static function is_first_order_clause( $query ) {
     811        return empty( $query ) || array_key_exists( 'terms', $query ) || array_key_exists( 'taxonomy', $query ) || array_key_exists( 'include_children', $query ) || array_key_exists( 'field', $query ) || array_key_exists( 'operator', $query );
    721812    }
    722813
     
    727818     * @access public
    728819     *
    729      * @param string $primary_table
    730      * @param string $primary_id_column
    731      * @return array
     820     * @param string $primary_table     Database table where the object being filtered is stored (eg wp_users).
     821     * @param string $primary_id_column ID column for the filtered object in $primary_table.
     822     * @return array {
     823     *     Array containing JOIN and WHERE SQL clauses to append to the main query.
     824     *
     825     *     @type string $join  SQL fragment to append to the main JOIN clause.
     826     *     @type string $where SQL fragment to append to the main WHERE clause.
     827     * }
    732828     */
    733829    public function get_sql( $primary_table, $primary_id_column ) {
     830        $this->primary_table = $primary_table;
     831        $this->primary_id_column = $primary_id_column;
     832
     833        return $this->get_sql_clauses();
     834    }
     835
     836    /**
     837     * Generate SQL clauses to be appended to a main query.
     838     *
     839     * Called by the public {@see WP_Tax_Query::get_sql()}, this method
     840     * is abstracted out to maintain parity with the other Query classes.
     841     *
     842     * @since 4.1.0
     843     * @access protected
     844     *
     845     * @return array {
     846     *     Array containing JOIN and WHERE SQL clauses to append to the main query.
     847     *
     848     *     @type string $join  SQL fragment to append to the main JOIN clause.
     849     *     @type string $where SQL fragment to append to the main WHERE clause.
     850     * }
     851     */
     852    protected function get_sql_clauses() {
     853        $sql = $this->get_sql_for_query( $this->queries );
     854
     855        if ( ! empty( $sql['where'] ) ) {
     856            $sql['where'] = ' AND ' . $sql['where'];
     857        }
     858
     859        return $sql;
     860    }
     861
     862    /**
     863     * Generate SQL clauses for a single query array.
     864     *
     865     * If nested subqueries are found, this method recurses the tree to
     866     * produce the properly nested SQL.
     867     *
     868     * @since 4.1.0
     869     * @access protected
     870     *
     871     * @param array $query Query to parse.
     872     * @param int   $depth Optional. Number of tree levels deep we currently are.
     873     *              Used to calculate indentation.
     874     * @return array {
     875     *     Array containing JOIN and WHERE SQL clauses to append to a single query array.
     876     *
     877     *     @type string $join  SQL fragment to append to the main JOIN clause.
     878     *     @type string $where SQL fragment to append to the main WHERE clause.
     879     * }
     880     */
     881    protected function get_sql_for_query( $query, $depth = 0 ) {
     882        $sql_chunks = array(
     883            'join'  => array(),
     884            'where' => array(),
     885        );
     886
     887        $sql = array(
     888            'join'  => '',
     889            'where' => '',
     890        );
     891
     892        $indent = '';
     893        for ( $i = 0; $i < $depth; $i++ ) {
     894            $indent .= "  ";
     895        }
     896
     897        foreach ( $query as $key => $clause ) {
     898            if ( 'relation' === $key ) {
     899                $relation = $query['relation'];
     900            } else if ( is_array( $clause ) ) {
     901
     902                // This is a first-order clause.
     903                if ( $this->is_first_order_clause( $clause ) ) {
     904                    $clause_sql = $this->get_sql_for_clause( $clause, $query );
     905
     906                    $where_count = count( $clause_sql['where'] );
     907                    if ( ! $where_count ) {
     908                        $sql_chunks['where'][] = '';
     909                    } else if ( 1 === $where_count ) {
     910                        $sql_chunks['where'][] = $clause_sql['where'][0];
     911                    } else {
     912                        $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )';
     913                    }
     914
     915                    $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] );
     916                // This is a subquery, so we recurse.
     917                } else {
     918                    $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 );
     919
     920                    $sql_chunks['where'][] = $clause_sql['where'];
     921                    $sql_chunks['join'][]  = $clause_sql['join'];
     922                }
     923            }
     924        }
     925
     926        // Filter to remove empties.
     927        $sql_chunks['join']  = array_filter( $sql_chunks['join'] );
     928        $sql_chunks['where'] = array_filter( $sql_chunks['where'] );
     929
     930        if ( empty( $relation ) ) {
     931            $relation = 'AND';
     932        }
     933
     934        // Filter duplicate JOIN clauses and combine into a single string.
     935        if ( ! empty( $sql_chunks['join'] ) ) {
     936            $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) );
     937        }
     938
     939        // Generate a single WHERE clause with proper brackets and indentation.
     940        if ( ! empty( $sql_chunks['where'] ) ) {
     941            $sql['where'] = '( ' . "\n  " . $indent . implode( ' ' . "\n  " . $indent . $relation . ' ' . "\n  " . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')';
     942        }
     943
     944        return $sql;
     945    }
     946
     947    /**
     948     * Generate SQL JOIN and WHERE clauses for a first-order query clause.
     949
     950     * @since 4.1.0
     951     * @access public
     952     *
     953     * @param  array $clause       Query clause.
     954     * @param  array $parent_query Parent query array.
     955     * @return array {
     956     *     Array containing JOIN and WHERE SQL clauses to append to a first-order query.
     957     *
     958     *     @type string $join  SQL fragment to append to the main JOIN clause.
     959     *     @type string $where SQL fragment to append to the main WHERE clause.
     960     * }
     961     */
     962    public function get_sql_for_clause( $clause, $parent_query ) {
    734963        global $wpdb;
    735964
     965        $sql = array(
     966            'where' => array(),
     967            'join'  => array(),
     968        );
     969
    736970        $join = '';
    737         $where = array();
    738         $i = 0;
    739         $count = count( $this->queries );
    740 
    741         foreach ( $this->queries as $index => $query ) {
    742             $this->clean_query( $query );
    743 
    744             if ( is_wp_error( $query ) ) {
     971
     972        $this->clean_query( $clause );
     973
     974        if ( is_wp_error( $clause ) ) {
     975            return self::$no_results;
     976        }
     977
     978        $terms = $clause['terms'];
     979        $operator = strtoupper( $clause['operator'] );
     980
     981        if ( 'IN' == $operator ) {
     982
     983            if ( empty( $terms ) ) {
    745984                return self::$no_results;
    746985            }
    747986
    748             $terms = $query['terms'];
    749             $operator = strtoupper( $query['operator'] );
    750 
    751             if ( 'IN' == $operator ) {
    752 
    753                 if ( empty( $terms ) ) {
    754                     if ( 'OR' == $this->relation ) {
    755                         if ( ( $index + 1 === $count ) && empty( $where ) ) {
    756                             return self::$no_results;
    757                         }
    758                         continue;
    759                     } else {
    760                         return self::$no_results;
    761                     }
    762                 }
    763 
    764                 $terms = implode( ',', $terms );
    765 
    766                 $alias = $i ? 'tt' . $i : $wpdb->term_relationships;
    767 
    768                 $join .= " INNER JOIN $wpdb->term_relationships";
    769                 $join .= $i ? " AS $alias" : '';
    770                 $join .= " ON ($primary_table.$primary_id_column = $alias.object_id)";
    771 
    772                 $where[] = "$alias.term_taxonomy_id $operator ($terms)";
    773             } elseif ( 'NOT IN' == $operator ) {
    774 
    775                 if ( empty( $terms ) ) {
    776                     continue;
    777                 }
    778 
    779                 $terms = implode( ',', $terms );
    780 
    781                 $where[] = "$primary_table.$primary_id_column NOT IN (
    782                     SELECT object_id
    783                     FROM $wpdb->term_relationships
    784                     WHERE term_taxonomy_id IN ($terms)
    785                 )";
    786             } elseif ( 'AND' == $operator ) {
    787 
    788                 if ( empty( $terms ) ) {
    789                     continue;
    790                 }
    791 
    792                 $num_terms = count( $terms );
    793 
    794                 $terms = implode( ',', $terms );
    795 
    796                 $where[] = "(
    797                     SELECT COUNT(1)
    798                     FROM $wpdb->term_relationships
    799                     WHERE term_taxonomy_id IN ($terms)
    800                     AND object_id = $primary_table.$primary_id_column
    801                 ) = $num_terms";
     987            $terms = implode( ',', $terms );
     988
     989            $i = count( $this->table_aliases );
     990            $alias = $i ? 'tt' . $i : $wpdb->term_relationships;
     991            $this->table_aliases[] = $alias;
     992
     993            $join .= " INNER JOIN $wpdb->term_relationships";
     994            $join .= $i ? " AS $alias" : '';
     995            $join .= " ON ($this->primary_table.$this->primary_id_column = $alias.object_id)";
     996
     997            $where = "$alias.term_taxonomy_id $operator ($terms)";
     998
     999        } elseif ( 'NOT IN' == $operator ) {
     1000
     1001            if ( empty( $terms ) ) {
     1002                continue;
    8021003            }
    8031004
    804             $i++;
    805         }
    806 
    807         if ( ! empty( $where ) ) {
    808             $where = ' AND ( ' . implode( " $this->relation ", $where ) . ' )';
    809         } else {
    810             $where = '';
    811         }
    812         return compact( 'join', 'where' );
    813     }
     1005            $terms = implode( ',', $terms );
     1006
     1007            $where = "$this->primary_table.$this->primary_id_column NOT IN (
     1008                SELECT object_id
     1009                FROM $wpdb->term_relationships
     1010                WHERE term_taxonomy_id IN ($terms)
     1011            )";
     1012
     1013        } elseif ( 'AND' == $operator ) {
     1014
     1015            if ( empty( $terms ) ) {
     1016                continue;
     1017            }
     1018
     1019            $num_terms = count( $terms );
     1020
     1021            $terms = implode( ',', $terms );
     1022
     1023            $where = "(
     1024                SELECT COUNT(1)
     1025                FROM $wpdb->term_relationships
     1026                WHERE term_taxonomy_id IN ($terms)
     1027                AND object_id = $this->primary_table.$this->primary_id_column
     1028            ) = $num_terms";
     1029        }
     1030
     1031        $sql['join'][]  = $join;
     1032        $sql['where'][] = $where;
     1033        return $sql;
     1034    }
     1035
    8141036
    8151037    /**
     
    8191041     * @access private
    8201042     *
    821      * @param array &$query The single query
     1043     * @param array &$query The single query.
    8221044     */
    8231045    private function clean_query( &$query ) {
     
    8591081     * @since 3.2.0
    8601082     *
    861      * @param array &$query The single query
    862      * @param string $resulting_field The resulting field
     1083     * @param array  &$query          The single query.
     1084     * @param string $resulting_field The resulting field. Accepts 'slug', 'name', 'term_taxonomy_id',
     1085     *                                or 'term_id'. Default: 'term_id'.
    8631086     */
    8641087    public function transform_query( &$query, $resulting_field ) {
  • trunk/tests/phpunit/tests/post/query.php

    r29890 r29891  
    15431543
    15441544    /**
     1545     * @ticket 29738
     1546     * @group taxonomy
     1547     */
     1548    public function test_tax_query_two_nested_queries() {
     1549        register_taxonomy( 'foo', 'post' );
     1550        register_taxonomy( 'bar', 'post' );
     1551
     1552        $foo_term_1 = $this->factory->term->create( array(
     1553            'taxonomy' => 'foo',
     1554        ) );
     1555        $foo_term_2 = $this->factory->term->create( array(
     1556            'taxonomy' => 'foo',
     1557        ) );
     1558        $bar_term_1 = $this->factory->term->create( array(
     1559            'taxonomy' => 'bar',
     1560        ) );
     1561        $bar_term_2 = $this->factory->term->create( array(
     1562            'taxonomy' => 'bar',
     1563        ) );
     1564
     1565        $p1 = $this->factory->post->create();
     1566        $p2 = $this->factory->post->create();
     1567        $p3 = $this->factory->post->create();
     1568
     1569        wp_set_object_terms( $p1, array( $foo_term_1 ), 'foo' );
     1570        wp_set_object_terms( $p1, array( $bar_term_1 ), 'bar' );
     1571        wp_set_object_terms( $p2, array( $foo_term_2 ), 'foo' );
     1572        wp_set_object_terms( $p2, array( $bar_term_2 ), 'bar' );
     1573        wp_set_object_terms( $p3, array( $foo_term_1 ), 'foo' );
     1574        wp_set_object_terms( $p3, array( $bar_term_2 ), 'bar' );
     1575
     1576        $q = new WP_Query( array(
     1577            'fields' => 'ids',
     1578            'update_post_meta_cache' => false,
     1579            'update_post_term_cache' => false,
     1580            'tax_query' => array(
     1581                'relation' => 'OR',
     1582                array(
     1583                    'relation' => 'AND',
     1584                    array(
     1585                        'taxonomy' => 'foo',
     1586                        'terms' => array( $foo_term_1 ),
     1587                        'field' => 'term_id',
     1588                    ),
     1589                    array(
     1590                        'taxonomy' => 'bar',
     1591                        'terms' => array( $bar_term_1 ),
     1592                        'field' => 'term_id',
     1593                    ),
     1594                ),
     1595                array(
     1596                    'relation' => 'AND',
     1597                    array(
     1598                        'taxonomy' => 'foo',
     1599                        'terms' => array( $foo_term_2 ),
     1600                        'field' => 'term_id',
     1601                    ),
     1602                    array(
     1603                        'taxonomy' => 'bar',
     1604                        'terms' => array( $bar_term_2 ),
     1605                        'field' => 'term_id',
     1606                    ),
     1607                ),
     1608            ),
     1609        ) );
     1610
     1611        _unregister_taxonomy( 'foo' );
     1612        _unregister_taxonomy( 'bar' );
     1613
     1614        $this->assertEqualSets( array( $p1, $p2 ), $q->posts );
     1615    }
     1616
     1617    /**
     1618     * @ticket 29738
     1619     * @group taxonomy
     1620     */
     1621    public function test_tax_query_one_nested_query_one_first_order_query() {
     1622        register_taxonomy( 'foo', 'post' );
     1623        register_taxonomy( 'bar', 'post' );
     1624
     1625        $foo_term_1 = $this->factory->term->create( array(
     1626            'taxonomy' => 'foo',
     1627        ) );
     1628        $foo_term_2 = $this->factory->term->create( array(
     1629            'taxonomy' => 'foo',
     1630        ) );
     1631        $bar_term_1 = $this->factory->term->create( array(
     1632            'taxonomy' => 'bar',
     1633        ) );
     1634        $bar_term_2 = $this->factory->term->create( array(
     1635            'taxonomy' => 'bar',
     1636        ) );
     1637
     1638        $p1 = $this->factory->post->create();
     1639        $p2 = $this->factory->post->create();
     1640        $p3 = $this->factory->post->create();
     1641
     1642        wp_set_object_terms( $p1, array( $foo_term_1 ), 'foo' );
     1643        wp_set_object_terms( $p1, array( $bar_term_1 ), 'bar' );
     1644        wp_set_object_terms( $p2, array( $foo_term_2 ), 'foo' );
     1645        wp_set_object_terms( $p2, array( $bar_term_2 ), 'bar' );
     1646        wp_set_object_terms( $p3, array( $foo_term_1 ), 'foo' );
     1647        wp_set_object_terms( $p3, array( $bar_term_2 ), 'bar' );
     1648
     1649        $q = new WP_Query( array(
     1650            'fields' => 'ids',
     1651            'update_post_meta_cache' => false,
     1652            'update_post_term_cache' => false,
     1653            'tax_query' => array(
     1654                'relation' => 'OR',
     1655                array(
     1656                    'taxonomy' => 'foo',
     1657                    'terms' => array( $foo_term_2 ),
     1658                    'field' => 'term_id',
     1659                ),
     1660                array(
     1661                    'relation' => 'AND',
     1662                    array(
     1663                        'taxonomy' => 'foo',
     1664                        'terms' => array( $foo_term_1 ),
     1665                        'field' => 'term_id',
     1666                    ),
     1667                    array(
     1668                        'taxonomy' => 'bar',
     1669                        'terms' => array( $bar_term_1 ),
     1670                        'field' => 'term_id',
     1671                    ),
     1672                ),
     1673            ),
     1674        ) );
     1675
     1676        _unregister_taxonomy( 'foo' );
     1677        _unregister_taxonomy( 'bar' );
     1678
     1679        $this->assertEqualSets( array( $p1, $p2 ), $q->posts );
     1680    }
     1681
     1682    /**
     1683     * @ticket 29738
     1684     * @group taxonomy
     1685     */
     1686    public function test_tax_query_one_double_nested_query_one_first_order_query() {
     1687        register_taxonomy( 'foo', 'post' );
     1688        register_taxonomy( 'bar', 'post' );
     1689
     1690        $foo_term_1 = $this->factory->term->create( array(
     1691            'taxonomy' => 'foo',
     1692        ) );
     1693        $foo_term_2 = $this->factory->term->create( array(
     1694            'taxonomy' => 'foo',
     1695        ) );
     1696        $bar_term_1 = $this->factory->term->create( array(
     1697            'taxonomy' => 'bar',
     1698        ) );
     1699        $bar_term_2 = $this->factory->term->create( array(
     1700            'taxonomy' => 'bar',
     1701        ) );
     1702
     1703        $p1 = $this->factory->post->create();
     1704        $p2 = $this->factory->post->create();
     1705        $p3 = $this->factory->post->create();
     1706        $p4 = $this->factory->post->create();
     1707
     1708        wp_set_object_terms( $p1, array( $foo_term_1 ), 'foo' );
     1709        wp_set_object_terms( $p1, array( $bar_term_1 ), 'bar' );
     1710        wp_set_object_terms( $p2, array( $foo_term_2 ), 'foo' );
     1711        wp_set_object_terms( $p2, array( $bar_term_2 ), 'bar' );
     1712        wp_set_object_terms( $p3, array( $foo_term_1 ), 'foo' );
     1713        wp_set_object_terms( $p3, array( $bar_term_2 ), 'bar' );
     1714
     1715        $q = new WP_Query( array(
     1716            'fields' => 'ids',
     1717            'update_post_meta_cache' => false,
     1718            'update_post_term_cache' => false,
     1719            'tax_query' => array(
     1720                'relation' => 'OR',
     1721                array(
     1722                    'taxonomy' => 'foo',
     1723                    'terms' => array( $foo_term_2 ),
     1724                    'field' => 'term_id',
     1725                ),
     1726                array(
     1727                    'relation' => 'AND',
     1728                    array(
     1729                        'taxonomy' => 'foo',
     1730                        'terms' => array( $foo_term_1 ),
     1731                        'field' => 'term_id',
     1732                    ),
     1733                    array(
     1734                        'relation' => 'OR',
     1735                        array(
     1736                            'taxonomy' => 'bar',
     1737                            'terms' => array( $bar_term_1 ),
     1738                            'field' => 'term_id',
     1739                        ),
     1740                        array(
     1741                            'taxonomy' => 'bar',
     1742                            'terms' => array( $bar_term_2 ),
     1743                            'field' => 'term_id',
     1744                        ),
     1745                    ),
     1746                ),
     1747            ),
     1748        ) );
     1749
     1750        _unregister_taxonomy( 'foo' );
     1751        _unregister_taxonomy( 'bar' );
     1752
     1753        $this->assertEqualSets( array( $p1, $p2, $p3 ), $q->posts );
     1754    }
     1755
     1756    /**
    15451757     * @ticket 20604
    15461758     * @group taxonomy
     
    19742186    /**
    19752187     * @group taxonomy
    1976      * @ticket 29718
     2188     * @ticket 29738
    19772189     */
    19782190    public function test_populate_taxonomy_query_var_from_tax_query() {
     
    20892301    /**
    20902302     * @group taxonomy
    2091      * @ticket 29718
     2303     * @ticket 29738
    20922304     */
    20932305    public function test_populate_cat_category_name_query_var_from_tax_query() {
     
    20972309        ) );
    20982310        $c = $this->factory->term->create( array(
    2099             'taxonomy' => 'foo',
     2311            'taxonomy' => 'category',
    21002312            'slug' => 'bar',
    21012313        ) );
     
    21312343    /**
    21322344     * @group taxonomy
    2133      * @ticket 29718
     2345     * @ticket 29738
    21342346     */
    21352347    public function test_populate_tag_id_query_var_from_tax_query() {
Note: See TracChangeset for help on using the changeset viewer.