WordPress.org

Make WordPress Core

Ticket #29738: 29738.patch

File 29738.patch, 20.3 KB (added by boonebgorges, 5 years ago)
  • src/wp-includes/query.php

    diff --git src/wp-includes/query.php src/wp-includes/query.php
    index a832e6d..8158404 100644
    class WP_Query { 
    16721672                        $this->parse_tax_query( $qv );
    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':
    16781682                                                        $this->is_category = true;
    class WP_Query { 
    26812685                        if ( empty($post_type) ) {
    26822686                                // Do a fully inclusive search for currently registered post types of queried taxonomies
    26832687                                $post_type = array();
    2684                                 $taxonomies = wp_list_pluck( $this->tax_query->queries, 'taxonomy' );
     2688                                $taxonomies = array_keys( $this->tax_query->queried_terms );
    26852689                                foreach ( get_post_types( array( 'exclude_from_search' => false ) ) as $pt ) {
    26862690                                        $object_taxonomies = $pt === 'attachment' ? get_taxonomies_for_attachments() : get_object_taxonomies( $pt );
    26872691                                        if ( array_intersect( $taxonomies, $object_taxonomies ) )
    class WP_Query { 
    26982702                        }
    26992703                }
    27002704
    2701                 // Back-compat
    2702                 if ( !empty($this->tax_query->queries) ) {
    2703                         $tax_query_in_and = wp_list_filter( $this->tax_query->queries, array( 'operator' => 'NOT IN' ), 'NOT' );
    2704                         if ( !empty( $tax_query_in_and ) ) {
    2705                                 if ( !isset( $q['taxonomy'] ) ) {
    2706                                         foreach ( $tax_query_in_and as $a_tax_query ) {
    2707                                                 if ( !in_array( $a_tax_query['taxonomy'], array( 'category', 'post_tag' ) ) ) {
    2708                                                         $q['taxonomy'] = $a_tax_query['taxonomy'];
    2709                                                         if ( 'slug' == $a_tax_query['field'] )
    2710                                                                 $q['term'] = $a_tax_query['terms'][0];
    2711                                                         else
    2712                                                                 $q['term_id'] = $a_tax_query['terms'][0];
     2705                // Ensure that 'taxonomy', 'term', 'term_id', 'cat', and
     2706                // 'category_name' vars are set for backward compatibility
     2707                if ( ! empty( $this->tax_query->queried_terms ) ) {
    27132708
    2714                                                         break;
    2715                                                 }
     2709                        // Set 'taxonomy', 'term', and 'term_id' to the
     2710                        // first taxonomy other than 'post_tag' or 'category'
     2711                        if ( ! isset( $q['taxonomy'] ) ) {
     2712                                foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
     2713                                        if ( empty( $queried_items['terms'][0] ) ) {
     2714                                                continue;
    27162715                                        }
    2717                                 }
    27182716
    2719                                 $cat_query = wp_list_filter( $tax_query_in_and, array( 'taxonomy' => 'category' ) );
    2720                                 if ( ! empty( $cat_query ) ) {
    2721                                         $cat_query = reset( $cat_query );
     2717                                        if ( ! in_array( $queried_taxonomy, array( 'category', 'post_tag' ) ) ) {
     2718                                                $q['taxonomy'] = $queried_taxonomy;
    27222719
    2723                                         if ( ! empty( $cat_query['terms'][0] ) ) {
    2724                                                 $the_cat = get_term_by( $cat_query['field'], $cat_query['terms'][0], 'category' );
    2725                                                 if ( $the_cat ) {
    2726                                                         $this->set( 'cat', $the_cat->term_id );
    2727                                                         $this->set( 'category_name', $the_cat->slug );
     2720                                                if ( 'slug' === $queried_items['field'] ) {
     2721                                                        $q['term'] = $queried_items['terms'][0];
     2722                                                } else {
     2723                                                        $q['term_id'] = $queried_items['terms'][0];
    27282724                                                }
    2729                                                 unset( $the_cat );
    27302725                                        }
    27312726                                }
    2732                                 unset( $cat_query );
     2727                        }
    27332728
    2734                                 $tag_query = wp_list_filter( $tax_query_in_and, array( 'taxonomy' => 'post_tag' ) );
    2735                                 if ( ! empty( $tag_query ) ) {
    2736                                         $tag_query = reset( $tag_query );
     2729                        // 'cat', 'category_name', 'tag_id'
     2730                        foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
     2731                                if ( empty( $queried_items['terms'][0] ) ) {
     2732                                        continue;
     2733                                }
     2734
     2735                                if ( 'category' === $queried_taxonomy ) {
     2736                                        $the_cat = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'category' );
     2737                                        if ( $the_cat ) {
     2738                                                $this->set( 'cat', $the_cat->term_id );
     2739                                                $this->set( 'category_name', $the_cat->slug );
     2740                                        }
     2741                                        unset( $the_cat );
     2742                                }
    27372743
    2738                                         if ( ! empty( $tag_query['terms'][0] ) ) {
    2739                                                 $the_tag = get_term_by( $tag_query['field'], $tag_query['terms'][0], 'post_tag' );
    2740                                                 if ( $the_tag )
    2741                                                         $this->set( 'tag_id', $the_tag->term_id );
    2742                                                 unset( $the_tag );
     2744                                if ( 'post_tag' === $queried_taxonomy ) {
     2745                                        $the_tag = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'post_tag' );
     2746                                        if ( $the_tag ) {
     2747                                                $this->set( 'tag_id', $the_tag->term_id );
    27432748                                        }
     2749                                        unset( $the_tag );
    27442750                                }
    2745                                 unset( $tag_query );
    27462751                        }
    27472752                }
    27482753
  • src/wp-includes/taxonomy.php

    diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php
    index 5846844..8a8f2ef 100644
    class WP_Tax_Query { 
    667667         * @access private
    668668         * @var string
    669669         */
    670         private static $no_results = array( 'join' => '', 'where' => ' AND 0 = 1' );
     670        private static $no_results = array( 'join' => array( '' ), 'where' => array( '0 = 1' ) );
     671
     672        /**
     673         * A flat list of table aliases used in the JOIN clauses.
     674         *
     675         * @since 4.1.0
     676         * @access protected
     677         * @var array
     678         */
     679        protected $table_aliases = array();
     680
     681        /**
     682         * Terms and taxonomies fetched by this query.
     683         *
     684         * We store this data in a flat array because they are referenced in a
     685         * number of places by WP_Query.
     686         *
     687         * @since 4.1.0
     688         * @access public
     689         * @var array
     690         */
     691        public $queried_terms = array();
    671692
    672693        /**
    673694         * Constructor.
    class WP_Tax_Query { 
    699720                        $this->relation = 'AND';
    700721                }
    701722
    702                 $defaults = array(
    703                         'taxonomy' => '',
    704                         'terms' => array(),
    705                         'include_children' => true,
    706                         'field' => 'term_id',
    707                         'operator' => 'IN',
    708                 );
    709 
    710                 foreach ( $tax_query as $query ) {
    711                         if ( ! is_array( $query ) )
    712                                 continue;
    713 
    714                         $query = array_merge( $defaults, $query );
    715 
    716                         $query['terms'] = (array) $query['terms'];
    717 
    718                         $this->queries[] = $query;
    719                 }
     723                $this->queries = $this->sanitize_query( $tax_query );
    720724        }
    721725
    722726        /**
    class WP_Tax_Query { 
    725729         * @since 3.1.0
    726730         * @access public
    727731         *
    728          * @param string $primary_table
    729          * @param string $primary_id_column
     732         * @param  string $primary_table
     733         * @param  string $primary_id_column
    730734         * @return array
    731735         */
    732736        public function get_sql( $primary_table, $primary_id_column ) {
    733                 global $wpdb;
     737                $this->primary_table = $primary_table;
     738                $this->primary_id_column = $primary_id_column;
    734739
    735                 $join = '';
    736                 $where = array();
    737                 $i = 0;
    738                 $count = count( $this->queries );
     740                $sql = $this->get_sql_for_query( $this->queries );
    739741
    740                 foreach ( $this->queries as $index => $query ) {
    741                         $this->clean_query( $query );
     742                if ( ! empty( $sql['where'] ) ) {
     743                        $sql['where'] = ' AND ' . "\n" . $sql['where'] . "\n";
     744                }
    742745
    743                         if ( is_wp_error( $query ) ) {
    744                                 return self::$no_results;
    745                         }
     746                return $sql;
     747        }
    746748
    747                         $terms = $query['terms'];
    748                         $operator = strtoupper( $query['operator'] );
     749        /**
     750         * Generate SQL clauses for a single tax_query query array.
     751         *
     752         * If nested subqueries are found, this method recurses the tree to
     753         * produce the properly nested SQL.
     754         *
     755         * @since 4.1.0
     756         *
     757         * @param  array $query Query to parse.
     758         * @param  int   $depth Optional. Number of tree levels deep we
     759         *               currently are. Used to calculate indentation.
     760         * @return array
     761         */
     762        protected function get_sql_for_query( $query, $depth = 0 ) {
     763                $sql_chunks = array(
     764                        'join'  => array(),
     765                        'where' => array(),
     766                );
    749767
    750                         if ( 'IN' == $operator ) {
     768                $sql = array(
     769                        'join'  => '',
     770                        'where' => '',
     771                );
    751772
    752                                 if ( empty( $terms ) ) {
    753                                         if ( 'OR' == $this->relation ) {
    754                                                 if ( ( $index + 1 === $count ) && empty( $where ) ) {
    755                                                         return self::$no_results;
    756                                                 }
    757                                                 continue;
     773                $indent = '';
     774                for ( $i = 0; $i < $depth; $i++ ) {
     775                        $indent .= "\t";
     776                }
     777
     778                foreach ( $query as $key => $clause ) {
     779                        if ( 'relation' === $key ) {
     780                                $relation = $query['relation'];
     781                        } else {
     782                                // This is a subquery
     783                                if ( isset( $clause['relation'] ) ) {
     784                                        $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 );
     785
     786                                        $sql_chunks['where'][] = $clause_sql['where'];
     787                                        $sql_chunks['join'][]  = $clause_sql['join'];
     788
     789                                // This is a first-order clause
     790                                } else {
     791                                        $clause_sql = $this->get_sql_for_clause( $clause, $query );
     792
     793                                        $where_count = count( $clause_sql['where'] );
     794                                        if ( ! $where_count ) {
     795                                                $sql_chunks['where'][] = '';
     796                                        } else if ( 1 === $where_count ) {
     797                                                $sql_chunks['where'][] = $clause_sql['where'][0];
    758798                                        } else {
    759                                                 return self::$no_results;
     799                                                $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )';
    760800                                        }
     801
     802                                        $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] );
    761803                                }
     804                        }
     805                }
    762806
    763                                 $terms = implode( ',', $terms );
     807                // Filter empties
     808                $sql_chunks['join']  = array_filter( $sql_chunks['join'] );
     809                $sql_chunks['where'] = array_filter( $sql_chunks['where'] );
    764810
    765                                 $alias = $i ? 'tt' . $i : $wpdb->term_relationships;
     811                if ( empty( $relation ) ) {
     812                        $relation = 'AND';
     813                }
    766814
    767                                 $join .= " INNER JOIN $wpdb->term_relationships";
    768                                 $join .= $i ? " AS $alias" : '';
    769                                 $join .= " ON ($primary_table.$primary_id_column = $alias.object_id)";
     815                if ( ! empty( $sql_chunks['join'] ) ) {
     816                        $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) );
     817                }
    770818
    771                                 $where[] = "$alias.term_taxonomy_id $operator ($terms)";
    772                         } elseif ( 'NOT IN' == $operator ) {
     819                if ( ! empty( $sql_chunks['where'] ) ) {
     820                        $sql['where'] = '( ' . "\n\t" . $indent . implode( ' ' . "\n\t" . $indent . $relation . ' ' . "\n\t" . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')' . "\n";
     821                }
    773822
    774                                 if ( empty( $terms ) ) {
    775                                         continue;
    776                                 }
     823                return $sql;
     824        }
    777825
    778                                 $terms = implode( ',', $terms );
     826        /**
     827         * Generate SQL JOIN and WHERE clauses for a first-order query clause.
     828         *
     829         * @param  array         $clause       Query clause.
     830         * @param  WP_Meta_Query $parent_query Query object.
     831         * @return array
     832         */
     833        public function get_sql_for_clause( $clause, $parent_query ) {
     834                global $wpdb;
    779835
    780                                 $where[] = "$primary_table.$primary_id_column NOT IN (
    781                                         SELECT object_id
    782                                         FROM $wpdb->term_relationships
    783                                         WHERE term_taxonomy_id IN ($terms)
    784                                 )";
    785                         } elseif ( 'AND' == $operator ) {
     836                $sql = array(
     837                        'where' => array(),
     838                        'join'  => array(),
     839                );
    786840
    787                                 if ( empty( $terms ) ) {
    788                                         continue;
    789                                 }
     841                $join = '';
     842
     843                $this->clean_query( $clause );
    790844
    791                                 $num_terms = count( $terms );
     845                if ( is_wp_error( $clause ) ) {
     846                        return self::$no_results;
     847                }
    792848
    793                                 $terms = implode( ',', $terms );
     849                $terms = $clause['terms'];
     850                $operator = strtoupper( $clause['operator'] );
    794851
    795                                 $where[] = "(
    796                                         SELECT COUNT(1)
    797                                         FROM $wpdb->term_relationships
    798                                         WHERE term_taxonomy_id IN ($terms)
    799                                         AND object_id = $primary_table.$primary_id_column
    800                                 ) = $num_terms";
     852                if ( 'IN' == $operator ) {
     853
     854                        if ( empty( $terms ) ) {
     855                                return self::$no_results;
     856                        }
     857
     858                        $terms = implode( ',', $terms );
     859
     860                        $i = count( $this->table_aliases );
     861                        $alias = $i ? 'tt' . $i : $wpdb->term_relationships;
     862                        $this->table_aliases[] = $alias;
     863
     864                        $join .= " INNER JOIN $wpdb->term_relationships";
     865                        $join .= $i ? " AS $alias" : '';
     866                        $join .= " ON ($this->primary_table.$this->primary_id_column = $alias.object_id)";
     867
     868                        $where = "$alias.term_taxonomy_id $operator ($terms)";
     869
     870                } elseif ( 'NOT IN' == $operator ) {
     871
     872                        if ( empty( $terms ) ) {
     873                                continue;
     874                        }
     875
     876                        $terms = implode( ',', $terms );
     877
     878                        $where = "$this->primary_table.$this->primary_id_column NOT IN (
     879                                SELECT object_id
     880                                FROM $wpdb->term_relationships
     881                                WHERE term_taxonomy_id IN ($terms)
     882                        )";
     883
     884                } elseif ( 'AND' == $operator ) {
     885
     886                        if ( empty( $terms ) ) {
     887                                continue;
    801888                        }
    802889
    803                         $i++;
     890                        $num_terms = count( $terms );
     891
     892                        $terms = implode( ',', $terms );
     893
     894                        $where = "(
     895                                SELECT COUNT(1)
     896                                FROM $wpdb->term_relationships
     897                                WHERE term_taxonomy_id IN ($terms)
     898                                AND object_id = $this->primary_table.$this->primary_id_column
     899                        ) = $num_terms";
    804900                }
    805901
    806                 if ( ! empty( $where ) ) {
    807                         $where = ' AND ( ' . implode( " $this->relation ", $where ) . ' )';
    808                 } else {
    809                         $where = '';
     902                $sql['join'][]  = $join;
     903                $sql['where'][] = $where;
     904                return $sql;
     905        }
     906
     907
     908        /**
     909         * Determine whether a clause is first-order.
     910         *
     911         * A "first-order" clause is one that contains any of the first-order
     912         * clause keys ('terms', 'taxonomy', 'include_children', 'field',
     913         * 'operator'). An empty clause also counts as a first-order clause,
     914         * for backward compatibility. Any clause that doesn't meet this is
     915         * determined, by process of elimination, to be a higher-order query.
     916         *
     917         * @since 4.1.0
     918         *
     919         * @param  array $q Clause to check.
     920         * @return bool
     921         */
     922        protected static function is_first_order_clause( $q ) {
     923                return empty( $q ) || array_key_exists( 'terms', $q ) || array_key_exists( 'taxonomy', $q ) || array_key_exists( 'include_children', $q ) || array_key_exists( 'field', $q ) || array_key_exists( 'operator', $q );
     924        }
     925
     926        /**
     927         * Recursive-friendly query sanitizer.
     928         *
     929         * Ensures that each query-level clause has a 'relation' key, and that
     930         * each first-order clause contains all the necessary keys from
     931         * $defaults.
     932         *
     933         * @since 4.1.0
     934         *
     935         * @param  array $query A tax_query query clause.
     936         * @return array
     937         */
     938        protected function sanitize_query( $query ) {
     939                $sanitized_query = array();
     940
     941                $defaults = array(
     942                        'taxonomy' => '',
     943                        'terms' => array(),
     944                        'include_children' => true,
     945                        'field' => 'term_id',
     946                        'operator' => 'IN',
     947                );
     948
     949                foreach ( $query as $key => $q ) {
     950                        if ( 'relation' === $key ) {
     951                                $sanitized_query['relation'] = $q;
     952                        } else if ( is_array( $q ) ) {
     953                                if ( ! self::is_first_order_clause( $q ) ) {
     954                                        $sanitized_query[] = $this->sanitize_query( $q );
     955                                } else {
     956                                        $sanitized_clause = array_merge( $defaults, $q );
     957                                        $sanitized_clause['terms'] = (array) $sanitized_clause['terms'];
     958                                        $sanitized_query[] = $sanitized_clause;
     959
     960                                        // Keep a copy of the clause in the
     961                                        // flat $queried_terms array, for use
     962                                        // in WP_Query
     963                                        if ( ! empty( $sanitized_clause['taxonomy'] ) && 'NOT IN' !== $sanitized_clause['operator'] ) {
     964                                                $taxonomy = $sanitized_clause['taxonomy'];
     965                                                if ( ! isset( $this->queried_terms[ $taxonomy ] ) ) {
     966                                                        $this->queried_terms[ $taxonomy ] = array();
     967                                                }
     968
     969                                                // Backward compatibility: Only store the first
     970                                                // 'terms' and 'field' found for a given taxonomy
     971                                                if ( isset( $sanitized_clause['terms'] ) && ! isset( $this->queried_terms[ $taxonomy ]['terms'] ) ) {
     972                                                        $this->queried_terms[ $taxonomy ]['terms'] = $sanitized_clause['terms'];
     973                                                }
     974
     975                                                if ( isset( $sanitized_clause['field'] ) && ! isset( $this->queried_terms[ $taxonomy ]['field'] ) ) {
     976                                                        $this->queried_terms[ $taxonomy ]['field'] = $sanitized_clause['field'];
     977                                                }
     978                                        }
     979                                }
     980                        }
    810981                }
    811                 return compact( 'join', 'where' );
     982
     983                return $sanitized_query;
    812984        }
    813985
    814986        /**
  • new file tests/phpunit/tests/term/query-nested.php

    diff --git tests/phpunit/tests/term/query-nested.php tests/phpunit/tests/term/query-nested.php
    new file mode 100644
    index 0000000..eb1d239
    - +  
     1<?php
     2
     3/**
     4 * @group taxonomy
     5 */
     6class Tests_Tax_Query_Nested extends WP_UnitTestCase {
     7        public function test_two_nested_queries() {
     8                register_taxonomy( 'foo', 'post' );
     9                register_taxonomy( 'bar', 'post' );
     10
     11                $foo_term_1 = $this->factory->term->create( array(
     12                        'taxonomy' => 'foo',
     13                ) );
     14                $foo_term_2 = $this->factory->term->create( array(
     15                        'taxonomy' => 'foo',
     16                ) );
     17                $bar_term_1 = $this->factory->term->create( array(
     18                        'taxonomy' => 'bar',
     19                ) );
     20                $bar_term_2 = $this->factory->term->create( array(
     21                        'taxonomy' => 'bar',
     22                ) );
     23
     24                $p1 = $this->factory->post->create();
     25                $p2 = $this->factory->post->create();
     26                $p3 = $this->factory->post->create();
     27
     28                wp_set_object_terms( $p1, array( $foo_term_1 ), 'foo' );
     29                wp_set_object_terms( $p1, array( $bar_term_1 ), 'bar' );
     30                wp_set_object_terms( $p2, array( $foo_term_2 ), 'foo' );
     31                wp_set_object_terms( $p2, array( $bar_term_2 ), 'bar' );
     32                wp_set_object_terms( $p3, array( $foo_term_1 ), 'foo' );
     33                wp_set_object_terms( $p3, array( $bar_term_2 ), 'bar' );
     34
     35                $q = new WP_Query( array(
     36                        'fields' => 'ids',
     37                        'update_post_meta_cache' => false,
     38                        'update_post_term_cache' => false,
     39                        'tax_query' => array(
     40                                'relation' => 'OR',
     41                                array(
     42                                        'relation' => 'AND',
     43                                        array(
     44                                                'taxonomy' => 'foo',
     45                                                'terms' => array( $foo_term_1 ),
     46                                                'field' => 'term_id',
     47                                        ),
     48                                        array(
     49                                                'taxonomy' => 'bar',
     50                                                'terms' => array( $bar_term_1 ),
     51                                                'field' => 'term_id',
     52                                        ),
     53                                ),
     54                                array(
     55                                        'relation' => 'AND',
     56                                        array(
     57                                                'taxonomy' => 'foo',
     58                                                'terms' => array( $foo_term_2 ),
     59                                                'field' => 'term_id',
     60                                        ),
     61                                        array(
     62                                                'taxonomy' => 'bar',
     63                                                'terms' => array( $bar_term_2 ),
     64                                                'field' => 'term_id',
     65                                        ),
     66                                ),
     67                        ),
     68                ) );
     69
     70                _unregister_taxonomy( 'foo' );
     71                _unregister_taxonomy( 'bar' );
     72
     73                $this->assertEqualSets( array( $p1, $p2 ), $q->posts );
     74        }
     75
     76        public function test_one_nested_query_one_first_order_query() {
     77                register_taxonomy( 'foo', 'post' );
     78                register_taxonomy( 'bar', 'post' );
     79
     80                $foo_term_1 = $this->factory->term->create( array(
     81                        'taxonomy' => 'foo',
     82                ) );
     83                $foo_term_2 = $this->factory->term->create( array(
     84                        'taxonomy' => 'foo',
     85                ) );
     86                $bar_term_1 = $this->factory->term->create( array(
     87                        'taxonomy' => 'bar',
     88                ) );
     89                $bar_term_2 = $this->factory->term->create( array(
     90                        'taxonomy' => 'bar',
     91                ) );
     92
     93                $p1 = $this->factory->post->create();
     94                $p2 = $this->factory->post->create();
     95                $p3 = $this->factory->post->create();
     96
     97                wp_set_object_terms( $p1, array( $foo_term_1 ), 'foo' );
     98                wp_set_object_terms( $p1, array( $bar_term_1 ), 'bar' );
     99                wp_set_object_terms( $p2, array( $foo_term_2 ), 'foo' );
     100                wp_set_object_terms( $p2, array( $bar_term_2 ), 'bar' );
     101                wp_set_object_terms( $p3, array( $foo_term_1 ), 'foo' );
     102                wp_set_object_terms( $p3, array( $bar_term_2 ), 'bar' );
     103
     104                $q = new WP_Query( array(
     105                        'fields' => 'ids',
     106                        'update_post_meta_cache' => false,
     107                        'update_post_term_cache' => false,
     108                        'tax_query' => array(
     109                                'relation' => 'OR',
     110                                array(
     111                                        'taxonomy' => 'foo',
     112                                        'terms' => array( $foo_term_2 ),
     113                                        'field' => 'term_id',
     114                                ),
     115                                array(
     116                                        'relation' => 'AND',
     117                                        array(
     118                                                'taxonomy' => 'foo',
     119                                                'terms' => array( $foo_term_1 ),
     120                                                'field' => 'term_id',
     121                                        ),
     122                                        array(
     123                                                'taxonomy' => 'bar',
     124                                                'terms' => array( $bar_term_1 ),
     125                                                'field' => 'term_id',
     126                                        ),
     127                                ),
     128                        ),
     129                ) );
     130
     131                _unregister_taxonomy( 'foo' );
     132                _unregister_taxonomy( 'bar' );
     133
     134                $this->assertEqualSets( array( $p1, $p2 ), $q->posts );
     135        }
     136
     137        public function test_one_double_nested_query_one_first_order_query() {
     138                register_taxonomy( 'foo', 'post' );
     139                register_taxonomy( 'bar', 'post' );
     140
     141                $foo_term_1 = $this->factory->term->create( array(
     142                        'taxonomy' => 'foo',
     143                ) );
     144                $foo_term_2 = $this->factory->term->create( array(
     145                        'taxonomy' => 'foo',
     146                ) );
     147                $bar_term_1 = $this->factory->term->create( array(
     148                        'taxonomy' => 'bar',
     149                ) );
     150                $bar_term_2 = $this->factory->term->create( array(
     151                        'taxonomy' => 'bar',
     152                ) );
     153
     154                $p1 = $this->factory->post->create();
     155                $p2 = $this->factory->post->create();
     156                $p3 = $this->factory->post->create();
     157                $p4 = $this->factory->post->create();
     158
     159                wp_set_object_terms( $p1, array( $foo_term_1 ), 'foo' );
     160                wp_set_object_terms( $p1, array( $bar_term_1 ), 'bar' );
     161                wp_set_object_terms( $p2, array( $foo_term_2 ), 'foo' );
     162                wp_set_object_terms( $p2, array( $bar_term_2 ), 'bar' );
     163                wp_set_object_terms( $p3, array( $foo_term_1 ), 'foo' );
     164                wp_set_object_terms( $p3, array( $bar_term_2 ), 'bar' );
     165
     166                $q = new WP_Query( array(
     167                        'fields' => 'ids',
     168                        'update_post_meta_cache' => false,
     169                        'update_post_term_cache' => false,
     170                        'tax_query' => array(
     171                                'relation' => 'OR',
     172                                array(
     173                                        'taxonomy' => 'foo',
     174                                        'terms' => array( $foo_term_2 ),
     175                                        'field' => 'term_id',
     176                                ),
     177                                array(
     178                                        'relation' => 'AND',
     179                                        array(
     180                                                'taxonomy' => 'foo',
     181                                                'terms' => array( $foo_term_1 ),
     182                                                'field' => 'term_id',
     183                                        ),
     184                                        array(
     185                                                'relation' => 'OR',
     186                                                array(
     187                                                        'taxonomy' => 'bar',
     188                                                        'terms' => array( $bar_term_1 ),
     189                                                        'field' => 'term_id',
     190                                                ),
     191                                                array(
     192                                                        'taxonomy' => 'bar',
     193                                                        'terms' => array( $bar_term_2 ),
     194                                                        'field' => 'term_id',
     195                                                ),
     196                                        ),
     197                                ),
     198                        ),
     199                ) );
     200
     201                _unregister_taxonomy( 'foo' );
     202                _unregister_taxonomy( 'bar' );
     203
     204                $this->assertEqualSets( array( $p1, $p2, $p3 ), $q->posts );
     205        }
     206}