Ticket #29642: 29642.patch
File 29642.patch, 20.3 KB (added by , 10 years ago) |
---|
-
src/wp-includes/meta.php
diff --git src/wp-includes/meta.php src/wp-includes/meta.php index e3c43cf..ba23352 100644
class WP_Meta_Query { 882 882 public $relation; 883 883 884 884 /** 885 * Table aliases used in the query currently being built. 886 * 887 * @access protected 888 * @var array 889 */ 890 protected $table_aliases = array(); 891 892 /** 893 * Database table to query for the metadata. 894 * 895 * @access public 896 * @var string 897 */ 898 public $meta_table; 899 900 /** 901 * Column in meta_table that represents the ID of the object the metadata belongs to. 902 * 903 * @access public 904 * @var string 905 */ 906 public $meta_id_column; 907 908 /** 909 * Database table that where the metadata's objects are stored (eg $wpdb->users) 910 * 911 * @access public 912 * @var string 913 */ 914 public $primary_table; 915 916 /** 917 * Column in primary_table that represents the ID of the object. 918 * 919 * @access public 920 * @var string 921 */ 922 public $primary_id_column; 923 924 /** 885 925 * Constructor 886 926 * 887 927 * @param array $meta_query (optional) A meta query … … class WP_Meta_Query { 896 936 $this->relation = 'AND'; 897 937 } 898 938 899 $this->queries = array(); 939 $this->queries = self::clean_queries( $meta_query ); 940 } 900 941 901 foreach ( $meta_query as $key => $query ) {902 if ( ! is_array( $query ) )903 continue;904 942 905 $this->queries[] = $query; 943 944 /** 945 * Ensure that the meta_query argument passed to the class constructor is well-formed. 946 * 947 * Eliminates empty items and ensures that a 'relation' is set. 948 * 949 * @param array $queries 950 * @return array 951 */ 952 public static function clean_queries( $queries ) { 953 $clean_queries = array(); 954 955 if ( ! is_array( $queries ) ) { 956 return $clean_queries; 957 } 958 959 foreach ( $queries as $key => $query ) { 960 if ( 'relation' === $key ) { 961 $relation = $query; 962 963 // The presence of a 'key' or 'value' key indicates 964 // that this is a first-order clause 965 } else if ( isset( $query['key'] ) || isset( $query['value'] ) ) { 966 $clean_queries[] = $query; 967 968 // Otherwise, it's a nested query 969 } else { 970 $cleaned_query = self::clean_queries( $query ); 971 972 if ( ! empty( $cleaned_query ) ) { 973 $clean_queries[] = $cleaned_query; 974 } 975 } 906 976 } 977 978 if ( empty( $clean_queries ) ) { 979 return $clean_queries; 980 } 981 982 // Sanitize the 'relation' key 983 if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) { 984 $clean_queries['relation'] = 'OR'; 985 986 // If there is only a single clause, call the relation 'OR'. 987 // This value will not actually be used to join clauses, but it 988 // simplifies the logic around combining key-only queries 989 } else if ( 1 === count( $clean_queries ) ) { 990 $clean_queries['relation'] = 'OR'; 991 } else { 992 $clean_queries['relation'] = 'AND'; 993 } 994 995 return $clean_queries; 907 996 } 908 997 909 998 /** … … class WP_Meta_Query { 958 1047 } 959 1048 960 1049 /** 961 * Generate s SQL clauses to be appended to a main query.1050 * Generate the SQL clauses for a query array. 962 1051 * 963 * @since 3.2.0964 * @access public1052 * In the case of a nested query, this method recurses to generate 1053 * properly nested SQL. 965 1054 * 966 * @param string $type Type of meta 967 * @param string $primary_table 968 * @param string $primary_id_column 969 * @param object $context (optional) The main query object 970 * @return array( 'join' => $join_sql, 'where' => $where_sql ) 1055 * @param array $query Query to parse. 1056 * @param int $depth Optional. Number of tree levels deep we 1057 * currently are. Used to calculate indentation. 1058 * @return array Array of 'join' and 'where' clauses. 971 1059 */ 972 public function get_sql( $type, $primary_table, $primary_id_column, $context = null ) { 973 global $wpdb; 974 975 if ( ! $meta_table = _get_meta_table( $type ) ) 976 return false; 977 978 $meta_id_column = sanitize_key( $type . '_id' ); 979 980 $join = array(); 981 $where = array(); 982 983 $key_only_queries = array(); 984 $queries = array(); 1060 public function get_sql_for_query( $query, $depth = 0 ) { 1061 $sql_chunks = array( 1062 'join' => array(), 1063 'where' => array(), 1064 ); 985 1065 986 // Split out the queries with empty arrays as value 987 foreach ( $this->queries as $k => $q ) { 988 if ( isset( $q['value'] ) && is_array( $q['value'] ) && empty( $q['value'] ) ) { 989 $key_only_queries[$k] = $q; 990 unset( $this->queries[$k] ); 991 } 992 } 1066 $sql = array( 1067 'join' => '', 1068 'where' => '', 1069 ); 993 1070 994 // Split out the meta_key only queries (we can only do this for OR) 995 if ( 'OR' == $this->relation ) { 996 foreach ( $this->queries as $k => $q ) { 997 if ( ( empty( $q['compare'] ) || 'NOT EXISTS' != $q['compare'] ) && ! array_key_exists( 'value', $q ) && ! empty( $q['key'] ) ) 998 $key_only_queries[$k] = $q; 999 else 1000 $queries[$k] = $q; 1001 } 1002 } else { 1003 $queries = $this->queries; 1071 $indent = ''; 1072 for ( $i = 0; $i < $depth; $i++ ) { 1073 $indent .= "\t"; 1004 1074 } 1005 1075 1006 // Specify all the meta_key only queries in one go 1007 if ( $key_only_queries ) { 1008 $join[] = "INNER JOIN $meta_table ON $primary_table.$primary_id_column = $meta_table.$meta_id_column"; 1076 foreach ( $query as $key => $clause ) { 1077 if ( 'relation' === $key ) { 1078 $relation = $query['relation']; 1079 } else { 1080 // This is a subquery 1081 if ( isset( $clause['relation'] ) ) { 1082 $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 ); 1083 1084 $sql_chunks['where'][] = $clause_sql['where']; 1085 $sql_chunks['join'][] = $clause_sql['join']; 1086 1087 1088 // This is a first-order clause 1089 } else { 1090 $clause_sql = $this->get_sql_for_clause( $clause, $query ); 1091 1092 $where_count = count( $clause_sql['where'] ); 1093 if ( ! $where_count ) { 1094 $sql_chunks['where'][] = ''; 1095 } else if ( 1 === $where_count ) { 1096 $sql_chunks['where'][] = $clause_sql['where'][0]; 1097 } else { 1098 $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )'; 1099 } 1100 $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] ); 1101 } 1009 1102 1010 foreach ( $key_only_queries as $key => $q ) 1011 $where["key-only-$key"] = $wpdb->prepare( "$meta_table.meta_key = %s", trim( $q['key'] ) ); 1103 } 1012 1104 } 1013 1105 1014 foreach ( $queries as $k => $q ) { 1015 $meta_key = isset( $q['key'] ) ? trim( $q['key'] ) : ''; 1016 $meta_type = $this->get_cast_for_type( isset( $q['type'] ) ? $q['type'] : '' ); 1017 1018 if ( array_key_exists( 'value', $q ) && is_null( $q['value'] ) ) 1019 $q['value'] = ''; 1106 $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) ); 1107 $sql['where'] = '( ' . "\n\t" . $indent . implode( ' ' . "\n\t" . $indent . $relation . ' ' . "\n\t" . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')' . "\n"; 1020 1108 1021 $meta_value = isset( $q['value'] ) ? $q['value'] : null; 1109 return $sql; 1110 } 1022 1111 1023 if ( isset( $q['compare'] ) ) 1024 $meta_compare = strtoupper( $q['compare'] ); 1025 else 1026 $meta_compare = is_array( $meta_value ) ? 'IN' : '='; 1112 /** 1113 * Generate SQL JOIN and WHERE clauses for a first-order query clause. 1114 * 1115 * "First-order" means that it's an array with a 'key' or 'value'. 1116 * 1117 * @param array $clause Query clause. 1118 * @param WP_Meta_Query $parent_query Query object. 1119 * @return array 1120 */ 1121 public function get_sql_for_clause( $clause, $parent_query ) { 1122 global $wpdb; 1027 1123 1028 if ( ! in_array( $meta_compare, array( 1029 '=', '!=', '>', '>=', '<', '<=', 1030 'LIKE', 'NOT LIKE', 1031 'IN', 'NOT IN', 1032 'BETWEEN', 'NOT BETWEEN', 1033 'NOT EXISTS', 1034 'REGEXP', 'NOT REGEXP', 'RLIKE' 1035 ) ) ) 1036 $meta_compare = '='; 1124 $sql_chunks = array( 1125 'where' => array(), 1126 'join' => array(), 1127 ); 1037 1128 1038 $i = count( $join);1039 $alias = $i ? 'mt' . $i : $meta_table;1129 $i = count( $this->table_aliases ); 1130 $alias = $i ? 'mt' . $i : $this->meta_table; 1040 1131 1041 if ( 'NOT EXISTS' == $meta_compare ) { 1042 $join[$i] = "LEFT JOIN $meta_table"; 1043 $join[$i] .= $i ? " AS $alias" : ''; 1044 $join[$i] .= " ON ($primary_table.$primary_id_column = $alias.$meta_id_column AND $alias.meta_key = '$meta_key')"; 1132 if ( isset( $clause['compare'] ) ) { 1133 $meta_compare = strtoupper( $clause['compare'] ); 1134 } else { 1135 $meta_compare = isset( $clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '='; 1136 } 1045 1137 1046 $where[$k] = ' ' . $alias . '.' . $meta_id_column . ' IS NULL'; 1138 if ( ! in_array( $meta_compare, array( 1139 '=', '!=', '>', '>=', '<', '<=', 1140 'LIKE', 'NOT LIKE', 1141 'IN', 'NOT IN', 1142 'BETWEEN', 'NOT BETWEEN', 1143 'EXISTS', 'NOT EXISTS', 1144 'REGEXP', 'NOT REGEXP', 'RLIKE' 1145 ) ) ) { 1146 $meta_compare = '='; 1147 } 1047 1148 1048 continue; 1149 // There are a number of different query structures that get 1150 // built in different ways 1151 // 1. Key-only clauses - (a) clauses without a 'value' key that 1152 // appear in the context of an OR relation and do not use 1153 // 'NOT EXISTS' as the 'compare', or (b) clauses with an 1154 // empty array for 'value' 1155 if ( ! empty( $clause['key'] ) && ( 1156 ( ! array_key_exists( 'value', $clause ) && 'NOT EXISTS' !== $meta_compare && 'OR' === $parent_query['relation'] ) || 1157 ( isset( $clause['value'] ) && is_array( $clause['value'] ) && empty( $clause['value'] ) ) 1158 ) ) { 1159 1160 $alias = $this->meta_table; 1161 $sql_chunks['join'][] = " INNER JOIN $this->meta_table ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column)"; 1162 $sql_chunks['where'][] = $wpdb->prepare( "$this->meta_table.meta_key = %s", trim( $clause['key'] ) ); 1163 1164 // 2. NOT EXISTS 1165 } else if ( 'NOT EXISTS' === $meta_compare ) { 1166 $join = " LEFT JOIN $this->meta_table"; 1167 $join .= $i ? " AS $alias" : ''; 1168 $join .= $wpdb->prepare( " ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column AND $alias.meta_key = %s )", $clause['key'] ); 1169 $sql_chunks['join'][] = $join; 1170 1171 $sql_chunks['where'][] = $alias . '.' . $this->meta_id_column . ' IS NULL'; 1172 1173 // 3. EXISTS and other key-only queries 1174 } else if ( 'EXISTS' === $meta_compare || ( ! empty( $clause['key'] ) && ! array_key_exists( 'value', $clause ) ) ) { 1175 $join = " INNER JOIN $this->meta_table"; 1176 $join .= $i ? " AS $alias" : ''; 1177 $join .= " ON ( $this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column )"; 1178 $sql_chunks['join'][] = $join; 1179 1180 $sql_chunks['where'][] = $wpdb->prepare( $alias . '.meta_key = %s', trim( $clause['key'] ) ); 1181 1182 // 4. Clauses that have a value 1183 } else if ( array_key_exists( 'value', $clause ) ) { 1184 $join = " INNER JOIN $this->meta_table"; 1185 $join .= $i ? " AS $alias" : ''; 1186 $join .= " ON ($this->primary_table.$this->primary_id_column = $alias.$this->meta_id_column)"; 1187 $sql_chunks['join'][] = $join; 1188 1189 if ( ! empty( $clause['key'] ) ) { 1190 $sql_chunks['where'][] = $wpdb->prepare( "$alias.meta_key = %s", trim( $clause['key'] ) ); 1049 1191 } 1050 1192 1051 $join[$i] = "INNER JOIN $meta_table"; 1052 $join[$i] .= $i ? " AS $alias" : ''; 1053 $join[$i] .= " ON ($primary_table.$primary_id_column = $alias.$meta_id_column)"; 1193 $meta_type = $this->get_cast_for_type( isset( $clause['type'] ) ? $clause['type'] : '' ); 1054 1194 1055 $where[$k] = ''; 1056 if ( !empty( $meta_key ) ) 1057 $where[$k] = $wpdb->prepare( "$alias.meta_key = %s", $meta_key ); 1058 1059 if ( is_null( $meta_value ) ) { 1060 if ( empty( $where[$k] ) ) 1061 unset( $join[$i] ); 1062 continue; 1063 } 1195 $meta_value = isset( $clause['value'] ) ? $clause['value'] : ''; 1064 1196 1065 1197 if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) { 1066 if ( ! is_array( $meta_value ) ) 1198 if ( ! is_array( $meta_value ) ) { 1067 1199 $meta_value = preg_split( '/[,\s]+/', $meta_value ); 1068 1069 if ( empty( $meta_value ) ) {1070 unset( $join[$i] );1071 continue;1072 1200 } 1073 1201 } else { 1074 1202 $meta_value = trim( $meta_value ); 1075 1203 } 1076 1204 1077 if ( 'IN' == substr( $meta_compare, -2 ) ) {1205 if ( 'IN' == substr( $meta_compare, -2 ) ) { 1078 1206 $meta_compare_string = '(' . substr( str_repeat( ',%s', count( $meta_value ) ), 1 ) . ')'; 1079 } elseif ( 'BETWEEN' == substr( $meta_compare, -7 ) ) {1207 } elseif ( 'BETWEEN' == substr( $meta_compare, -7 ) ) { 1080 1208 $meta_value = array_slice( $meta_value, 0, 2 ); 1081 1209 $meta_compare_string = '%s AND %s'; 1082 1210 } elseif ( 'LIKE' == $meta_compare || 'NOT LIKE' == $meta_compare ) { … … class WP_Meta_Query { 1086 1214 $meta_compare_string = '%s'; 1087 1215 } 1088 1216 1089 if ( ! empty( $where[$k] ) )1090 $where[$k] .= ' AND ';1217 $sql_chunks['where'][] = $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string}", $meta_value ); 1218 } 1091 1219 1092 $where[$k] = ' (' . $where[$k] . $wpdb->prepare( "CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string})", $meta_value ); 1220 // Multiple WHERE clauses (for meta_key and meta_value) should 1221 // be joined in parentheses 1222 if ( 1 < count( $sql_chunks['where'] ) ) { 1223 $sql_chunks['where'] = array( '( ' . implode( ' AND ', $sql_chunks['where'] ) . ' )' ); 1093 1224 } 1094 1225 1095 $ where = array_filter( $where );1226 $this->table_aliases[] = $alias; 1096 1227 1097 if ( empty( $where ) ) 1098 $where = ''; 1099 else 1100 $where = ' AND (' . implode( "\n{$this->relation} ", $where ) . ' )'; 1228 return $sql_chunks; 1229 } 1230 1231 /** 1232 * Generates SQL clauses to be appended to a main query. 1233 * 1234 * @since 3.2.0 1235 * @access public 1236 * 1237 * @param string $type Type of meta 1238 * @param string $primary_table 1239 * @param string $primary_id_column 1240 * @param object $context (optional) The main query object 1241 * @return array( 'join' => $join_sql, 'where' => $where_sql ) 1242 */ 1243 public function get_sql( $type, $primary_table, $primary_id_column, $context = null ) { 1244 global $wpdb; 1101 1245 1102 $join = implode( "\n", $join ); 1103 if ( ! empty( $join ) ) 1104 $join = ' ' . $join; 1246 if ( ! $meta_table = _get_meta_table( $type ) ) { 1247 return false; 1248 } 1249 1250 $this->meta_table = $meta_table; 1251 $this->meta_id_column = sanitize_key( $type . '_id' ); 1252 1253 $this->primary_table = $primary_table; 1254 $this->primary_id_column = $primary_id_column; 1255 1256 $sql = $this->get_sql_for_query( $this->queries ); 1257 1258 if ( ! empty( $sql['where'] ) ) { 1259 $sql['where'] = ' AND ' . "\n" . $sql['where'] . "\n"; 1260 } 1105 1261 1106 1262 /** 1107 1263 * Filter the meta query's generated SQL. … … class WP_Meta_Query { 1119 1275 * @type object $context The main query object. 1120 1276 * } 1121 1277 */ 1122 return apply_filters_ref_array( 'get_meta_sql', array( compact( 'join', 'where' ), $this->queries, $type, $primary_table, $primary_id_column, $context ) );1278 return apply_filters_ref_array( 'get_meta_sql', array( $sql, $this->queries, $type, $primary_table, $primary_id_column, $context ) ); 1123 1279 } 1124 1280 } 1125 1281 -
new file tests/phpunit/tests/meta/nested.php
diff --git tests/phpunit/tests/meta/nested.php tests/phpunit/tests/meta/nested.php new file mode 100644 index 0000000..7e9524d
- + 1 <?php 2 3 /** 4 * @group meta 5 */ 6 class WP_Tests_Nested_Meta extends WP_UnitTestCase { 7 public function test_clean_queries_single_query() { 8 $expected = array( 9 'relation' => 'OR', 10 array( 11 'key' => 'foo', 12 'value' => 'bar', 13 ), 14 ); 15 16 $found = WP_Meta_Query::clean_queries( array( 17 array( 18 'key' => 'foo', 19 'value' => 'bar', 20 ), 21 ) ); 22 23 $this->assertEquals( $expected, $found ); 24 } 25 26 public function test_clean_queries_multiple_first_order_queries_relation_default() { 27 $expected = array( 28 'relation' => 'AND', 29 array( 30 'key' => 'foo', 31 'value' => 'bar', 32 ), 33 array( 34 'key' => 'foo2', 35 'value' => 'bar2', 36 ), 37 ); 38 39 $found = WP_Meta_Query::clean_queries( array( 40 array( 41 'key' => 'foo', 42 'value' => 'bar', 43 ), 44 array( 45 'key' => 'foo2', 46 'value' => 'bar2', 47 ), 48 ) ); 49 50 $this->assertEquals( $expected, $found ); 51 } 52 53 public function test_clean_queries_multiple_first_order_queries_relation_or() { 54 $expected = array( 55 'relation' => 'OR', 56 array( 57 'key' => 'foo', 58 'value' => 'bar', 59 ), 60 array( 61 'key' => 'foo2', 62 'value' => 'bar2', 63 ), 64 ); 65 66 $found = WP_Meta_Query::clean_queries( array( 67 'relation' => 'OR', 68 array( 69 'key' => 'foo', 70 'value' => 'bar', 71 ), 72 array( 73 'key' => 'foo2', 74 'value' => 'bar2', 75 ), 76 ) ); 77 78 $this->assertEquals( $expected, $found ); 79 } 80 81 public function test_clean_queries_multiple_first_order_queries_relation_or_lowercase() { 82 $expected = array( 83 'relation' => 'OR', 84 array( 85 'key' => 'foo', 86 'value' => 'bar', 87 ), 88 array( 89 'key' => 'foo2', 90 'value' => 'bar2', 91 ), 92 ); 93 94 $found = WP_Meta_Query::clean_queries( array( 95 'relation' => 'or', 96 array( 97 'key' => 'foo', 98 'value' => 'bar', 99 ), 100 array( 101 'key' => 'foo2', 102 'value' => 'bar2', 103 ), 104 ) ); 105 106 $this->assertEquals( $expected, $found ); 107 } 108 109 public function test_clean_queries_multiple_first_order_queries_invalid_relation() { 110 $expected = array( 111 'relation' => 'AND', 112 array( 113 'key' => 'foo', 114 'value' => 'bar', 115 ), 116 array( 117 'key' => 'foo2', 118 'value' => 'bar2', 119 ), 120 ); 121 122 $found = WP_Meta_Query::clean_queries( array( 123 'relation' => 'FOO', 124 array( 125 'key' => 'foo', 126 'value' => 'bar', 127 ), 128 array( 129 'key' => 'foo2', 130 'value' => 'bar2', 131 ), 132 ) ); 133 134 $this->assertEquals( $expected, $found ); 135 } 136 137 public function test_clean_queries_single_query_which_is_a_nested_query() { 138 $expected = array( 139 'relation' => 'OR', 140 array( 141 'relation' => 'AND', 142 array( 143 'key' => 'foo', 144 'value' => 'bar', 145 ), 146 array( 147 'key' => 'foo2', 148 'value' => 'bar2', 149 ), 150 ) 151 ); 152 153 $found = WP_Meta_Query::clean_queries( array( 154 array( 155 array( 156 'key' => 'foo', 157 'value' => 'bar', 158 ), 159 array( 160 'key' => 'foo2', 161 'value' => 'bar2', 162 ), 163 ), 164 ) ); 165 166 $this->assertEquals( $expected, $found ); 167 } 168 169 public function test_clean_queries_multiple_nested_queries() { 170 $expected = array( 171 'relation' => 'OR', 172 array( 173 'relation' => 'AND', 174 array( 175 'key' => 'foo', 176 'value' => 'bar', 177 ), 178 array( 179 'key' => 'foo2', 180 'value' => 'bar2', 181 ), 182 ), 183 array( 184 'relation' => 'AND', 185 array( 186 'key' => 'foo3', 187 'value' => 'bar3', 188 ), 189 array( 190 'key' => 'foo4', 191 'value' => 'bar4', 192 ), 193 ), 194 ); 195 196 $found = WP_Meta_Query::clean_queries( array( 197 'relation' => 'OR', 198 array( 199 array( 200 'key' => 'foo', 201 'value' => 'bar', 202 ), 203 array( 204 'key' => 'foo2', 205 'value' => 'bar2', 206 ), 207 ), 208 array( 209 array( 210 'key' => 'foo3', 211 'value' => 'bar3', 212 ), 213 array( 214 'key' => 'foo4', 215 'value' => 'bar4', 216 ), 217 ), 218 ) ); 219 220 $this->assertEquals( $expected, $found ); 221 } 222 223 public function test_meta_query_nested() { 224 $p1 = $this->factory->post->create(); 225 $p2 = $this->factory->post->create(); 226 $p3 = $this->factory->post->create(); 227 228 add_post_meta( $p1, 'foo', 'bar' ); 229 add_post_meta( $p2, 'foo2', 'bar' ); 230 add_post_meta( $p3, 'foo2', 'bar' ); 231 add_post_meta( $p3, 'foo3', 'bar' ); 232 233 $query = new WP_Query( array( 234 'update_post_meta_cache' => false, 235 'update_term_meta_cache' => false, 236 'fields' => 'ids', 237 'meta_query' => array( 238 'relation' => 'OR', 239 array( 240 'key' => 'foo', 241 'value' => 'bar', 242 ), 243 array( 244 'relation' => 'AND', 245 array( 246 'key' => 'foo2', 247 'value' => 'bar', 248 ), 249 array( 250 'key' => 'foo3', 251 'value' => 'bar', 252 ), 253 ), 254 ), 255 ) ); 256 257 $expected = array( $p1, $p3 ); 258 $this->assertEqualSets( $expected, $query->posts ); 259 } 260 261 public function test_meta_query_nested_two_levels_deep() { 262 $p1 = $this->factory->post->create(); 263 $p2 = $this->factory->post->create(); 264 $p3 = $this->factory->post->create(); 265 266 add_post_meta( $p1, 'foo', 'bar' ); 267 add_post_meta( $p3, 'foo2', 'bar' ); 268 add_post_meta( $p3, 'foo3', 'bar' ); 269 add_post_meta( $p3, 'foo4', 'bar' ); 270 271 $query = new WP_Query( array( 272 'update_post_meta_cache' => false, 273 'update_term_meta_cache' => false, 274 'fields' => 'ids', 275 'meta_query' => array( 276 'relation' => 'OR', 277 array( 278 'key' => 'foo', 279 'value' => 'bar', 280 ), 281 array( 282 'relation' => 'OR', 283 array( 284 'key' => 'foo2', 285 'value' => 'bar', 286 ), 287 array( 288 'relation' => 'AND', 289 array( 290 'key' => 'foo3', 291 'value' => 'bar', 292 ), 293 array( 294 'key' => 'foo4', 295 'value' => 'bar', 296 ), 297 ), 298 ), 299 ), 300 ) ); 301 302 $expected = array( $p1, $p3 ); 303 $this->assertEqualSets( $expected, $query->posts ); 304 } 305 }