Changeset 46188
- Timestamp:
- 09/19/2019 03:02:20 PM (5 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-wp-meta-query.php
r45603 r46188 101 101 * @since 4.2.0 Introduced support for naming query clauses by associative array keys. 102 102 * @since 5.1.0 Introduced $compare_key clause parameter, which enables LIKE key matches. 103 * @since 5.3.0 Increased the number of operators available to $compare_key. Introduced $type_key, 104 * which enables the $key to be cast to a new data type for comparisons. 103 105 * 104 106 * @param array $meta_query { … … 112 114 * 113 115 * @type string $key Meta key to filter by. 114 * @type string $compare_key MySQL operator used for comparing the $key. Accepts '=' and 'LIKE'. 115 * Default '='. 116 * @type string $compare_key MySQL operator used for comparing the $key. Accepts '=', '!=' 117 * 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'REGEXP', 'NOT REGEXP', 'RLIKE', 118 * 'EXISTS' (alias of '=') or 'NOT EXISTS' (alias of '!='). 119 * Default is 'IN' when `$key` is an array, '=' otherwise. 120 * @type string $type_key MySQL data type that the meta_key column will be CAST to for 121 * comparisons. Accepts 'BINARY' for case-sensitive regular expression 122 * comparisons. Default is ''. 116 123 * @type string $value Meta value to filter by. 117 124 * @type string $compare MySQL operator used for comparing the $value. Accepts '=', … … 240 247 */ 241 248 $primary_meta_query = array(); 242 foreach ( array( 'key', 'compare', 'type', 'compare_key' ) as $key ) {249 foreach ( array( 'key', 'compare', 'type', 'compare_key', 'type_key' ) as $key ) { 243 250 if ( ! empty( $qv[ "meta_$key" ] ) ) { 244 251 $primary_meta_query[ $key ] = $qv[ "meta_$key" ]; … … 499 506 } 500 507 501 if ( ! in_array( 502 $clause['compare'], 503 array( 504 '=', 505 '!=', 506 '>', 507 '>=', 508 '<', 509 '<=', 510 'LIKE', 511 'NOT LIKE', 512 'IN', 513 'NOT IN', 514 'BETWEEN', 515 'NOT BETWEEN', 516 'EXISTS', 517 'NOT EXISTS', 518 'REGEXP', 519 'NOT REGEXP', 520 'RLIKE', 521 ) 522 ) ) { 508 $non_numeric_operators = array( 509 '=', 510 '!=', 511 'LIKE', 512 'NOT LIKE', 513 'IN', 514 'NOT IN', 515 'EXISTS', 516 'NOT EXISTS', 517 'RLIKE', 518 'REGEXP', 519 'NOT REGEXP', 520 ); 521 522 $numeric_operators = array( 523 '>', 524 '>=', 525 '<', 526 '<=', 527 'BETWEEN', 528 'NOT BETWEEN', 529 ); 530 531 if ( ! in_array( $clause['compare'], $non_numeric_operators, true ) && ! in_array( $clause['compare'], $numeric_operators, true ) ) { 523 532 $clause['compare'] = '='; 524 533 } 525 534 526 if ( isset( $clause['compare_key'] ) && 'LIKE' === strtoupper( $clause['compare_key'] )) {535 if ( isset( $clause['compare_key'] ) ) { 527 536 $clause['compare_key'] = strtoupper( $clause['compare_key'] ); 528 537 } else { 538 $clause['compare_key'] = isset( $clause['key'] ) && is_array( $clause['key'] ) ? 'IN' : '='; 539 } 540 541 if ( ! in_array( $clause['compare_key'], $non_numeric_operators, true ) ) { 529 542 $clause['compare_key'] = '='; 530 543 } … … 595 608 $sql_chunks['where'][] = $alias . '.' . $this->meta_id_column . ' IS NULL'; 596 609 } else { 597 if ( 'LIKE' === $meta_compare_key ) { 598 $sql_chunks['where'][] = $wpdb->prepare( "$alias.meta_key LIKE %s", '%' . $wpdb->esc_like( trim( $clause['key'] ) ) . '%' ); 599 } else { 600 $sql_chunks['where'][] = $wpdb->prepare( "$alias.meta_key = %s", trim( $clause['key'] ) ); 610 /** 611 * In joined clauses negative operators have to be nested into a 612 * NOT EXISTS clause and flipped, to avoid returning records with 613 * matching post IDs but different meta keys. Here we prepare the 614 * nested clause. 615 */ 616 if ( in_array( $meta_compare_key, array( '!=', 'NOT IN', 'NOT LIKE', 'NOT EXISTS', 'NOT REGEXP' ), true ) ) { 617 // Negative clauses may be reused. 618 $i = count( $this->table_aliases ); 619 $subquery_alias = $i ? 'mt' . $i : $this->meta_table; 620 $this->table_aliases[] = $subquery_alias; 621 622 $meta_compare_string_start = 'NOT EXISTS ('; 623 $meta_compare_string_start .= "SELECT 1 FROM $wpdb->postmeta $subquery_alias "; 624 $meta_compare_string_start .= "WHERE $subquery_alias.post_ID = $alias.post_ID "; 625 $meta_compare_string_end = 'LIMIT 1'; 626 $meta_compare_string_end .= ')'; 601 627 } 628 629 switch ( $meta_compare_key ) { 630 case '=': 631 case 'EXISTS': 632 $where = $wpdb->prepare( "$alias.meta_key = %s", trim( $clause['key'] ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 633 break; 634 case 'LIKE': 635 $meta_compare_value = '%' . $wpdb->esc_like( trim( $clause['key'] ) ) . '%'; 636 $where = $wpdb->prepare( "$alias.meta_key LIKE %s", $meta_compare_value ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 637 break; 638 case 'IN': 639 $meta_compare_string = "$alias.meta_key IN (" . substr( str_repeat( ',%s', count( $clause['key'] ) ), 1 ) . ')'; 640 $where = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 641 break; 642 case 'RLIKE': 643 case 'REGEXP': 644 $operator = $meta_compare_key; 645 if ( isset( $clause['type_key'] ) && 'BINARY' === strtoupper( $clause['type_key'] ) ) { 646 $cast = 'BINARY'; 647 } else { 648 $cast = ''; 649 } 650 $where = $wpdb->prepare( "$alias.meta_key $operator $cast %s", trim( $clause['key'] ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 651 break; 652 653 case '!=': 654 case 'NOT EXISTS': 655 $meta_compare_string = $meta_compare_string_start . "AND $subquery_alias.meta_key = %s " . $meta_compare_string_end; 656 $where = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 657 break; 658 case 'NOT LIKE': 659 $meta_compare_string = $meta_compare_string_start . "AND $subquery_alias.meta_key LIKE %s " . $meta_compare_string_end; 660 661 $meta_compare_value = '%' . $wpdb->esc_like( trim( $clause['key'] ) ) . '%'; 662 $where = $wpdb->prepare( $meta_compare_string, $meta_compare_value ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 663 break; 664 case 'NOT IN': 665 $array_subclause = '(' . substr( str_repeat( ',%s', count( $clause['key'] ) ), 1 ) . ') '; 666 $meta_compare_string = $meta_compare_string_start . "AND $subquery_alias.meta_key IN " . $array_subclause . $meta_compare_string_end; 667 $where = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 668 break; 669 case 'NOT REGEXP': 670 $operator = $meta_compare_key; 671 if ( isset( $clause['type_key'] ) && 'BINARY' === strtoupper( $clause['type_key'] ) ) { 672 $cast = 'BINARY'; 673 } else { 674 $cast = ''; 675 } 676 677 $meta_compare_string = $meta_compare_string_start . "AND $subquery_alias.meta_key REGEXP $cast %s " . $meta_compare_string_end; 678 $where = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 679 break; 680 } 681 682 $sql_chunks['where'][] = $where; 602 683 } 603 684 } -
trunk/src/wp-includes/class-wp-query.php
r46144 r46188 615 615 * @since 4.9.0 Introduced the `$comment_count` parameter. 616 616 * @since 5.1.0 Introduced the `$meta_compare_key` parameter. 617 * @since 5.3.0 Introduced the `$meta_type_key` parameter. 617 618 * 618 619 * @param string|array $query { … … 656 657 * @type string $meta_value Custom field value. 657 658 * @type int $meta_value_num Custom field value number. 659 * @type string $meta_type_key Cast for 'meta_key'. See WP_Meta_Query::construct(). 658 660 * @type int $menu_order The menu order of the posts. 659 661 * @type int $monthnum The two-digit month. Default empty. Accepts numbers 1-12. -
trunk/tests/phpunit/tests/meta/query.php
r42343 r46188 739 739 740 740 /** 741 * Verifies only that meta_type_key is passed. See query/metaQuery.php for more complete tests. 742 * 743 * @ticket 43446 744 */ 745 public function test_meta_type_key_should_be_passed_to_meta_query() { 746 $posts = self::factory()->post->create_many( 3 ); 747 748 add_post_meta( $posts[0], 'AAA_FOO_AAA', 'abc' ); 749 add_post_meta( $posts[1], 'aaa_bar_aaa', 'abc' ); 750 add_post_meta( $posts[2], 'aaa_foo_bbb', 'abc' ); 751 add_post_meta( $posts[2], 'aaa_foo_aaa', 'abc' ); 752 753 $q = new WP_Query( 754 array( 755 'meta_key' => 'AAA_foo_.*', 756 'meta_compare_key' => 'REGEXP', 757 'fields' => 'ids', 758 ) 759 ); 760 761 $this->assertEqualSets( array( $posts[0], $posts[2] ), $q->posts ); 762 763 $q = new WP_Query( 764 array( 765 'meta_key' => 'AAA_FOO_.*', 766 'meta_compare_key' => 'REGEXP', 767 'meta_type_key' => 'BINARY', 768 'fields' => 'ids', 769 'fields' => 'ids', 770 ) 771 ); 772 773 $this->assertEqualSets( array( $posts[0] ), $q->posts ); 774 } 775 776 /** 741 777 * This is the clause that ensures that empty arrays are not valid queries. 742 778 */ -
trunk/tests/phpunit/tests/query/metaQuery.php
r43571 r46188 1923 1923 1924 1924 } 1925 1926 /** 1927 * @ticket 43446 1928 */ 1929 public function test_compare_key_not_equals() { 1930 $posts = self::factory()->post->create_many( 3 ); 1931 1932 add_post_meta( $posts[0], 'aaa_foo_aaa', 'abc' ); 1933 add_post_meta( $posts[1], 'aaa_bar_aaa', 'abc' ); 1934 add_post_meta( $posts[2], 'aaa_foo_bbb', 'abc' ); 1935 add_post_meta( $posts[2], 'aaa_foo_ccc', 'abc' ); 1936 1937 $q = new WP_Query( 1938 array( 1939 'meta_query' => array( 1940 array( 1941 'compare_key' => '!=', 1942 'key' => 'aaa_foo_bbb', 1943 'value' => 'abc', 1944 ), 1945 ), 1946 'fields' => 'ids', 1947 ) 1948 ); 1949 1950 $this->assertEqualSets( array( $posts[0], $posts[1] ), $q->posts ); 1951 } 1952 1953 /** 1954 * @ticket 43446 1955 */ 1956 public function test_compare_key_not_like() { 1957 $posts = self::factory()->post->create_many( 3 ); 1958 1959 add_post_meta( $posts[0], 'aaa_foo_aaa', 'abc' ); 1960 add_post_meta( $posts[1], 'aaa_bar_aaa', 'abc' ); 1961 add_post_meta( $posts[1], 'aaa_bar_ccc', 'abc' ); 1962 add_post_meta( $posts[2], 'aaa_foo_bbb', 'abc' ); 1963 1964 $q = new WP_Query( 1965 array( 1966 'meta_query' => array( 1967 array( 1968 'compare_key' => 'NOT LIKE', 1969 'key' => 'aaa_bar', 1970 'value' => 'abc', 1971 ), 1972 ), 1973 'fields' => 'ids', 1974 ) 1975 ); 1976 1977 $this->assertEqualSets( array( $posts[0], $posts[2] ), $q->posts ); 1978 } 1979 1980 /** 1981 * @ticket 43446 1982 */ 1983 public function test_compare_key_in() { 1984 $posts = self::factory()->post->create_many( 3 ); 1985 1986 add_post_meta( $posts[0], 'aaa_foo_aaa', 'abc' ); 1987 add_post_meta( $posts[1], 'aaa_bar_aaa', 'abc' ); 1988 add_post_meta( $posts[2], 'aaa_foo_bbb', 'abc' ); 1989 1990 $q = new WP_Query( 1991 array( 1992 'meta_query' => array( 1993 array( 1994 'compare_key' => 'IN', 1995 'key' => array( 'aaa_foo_bbb', 'aaa_bar_aaa' ), 1996 ), 1997 ), 1998 'fields' => 'ids', 1999 ) 2000 ); 2001 2002 $this->assertEqualSets( array( $posts[1], $posts[2] ), $q->posts ); 2003 } 2004 2005 /** 2006 * @ticket 43446 2007 */ 2008 public function test_compare_key_not_in() { 2009 $posts = self::factory()->post->create_many( 3 ); 2010 2011 add_post_meta( $posts[0], 'aaa_foo_aaa', 'abc' ); 2012 add_post_meta( $posts[0], 'aaa_foo_ddd', 'abc' ); 2013 add_post_meta( $posts[1], 'aaa_bar_aaa', 'abc' ); 2014 add_post_meta( $posts[2], 'aaa_foo_bbb', 'abc' ); 2015 add_post_meta( $posts[2], 'aaa_foo_ccc', 'abc' ); 2016 2017 $q = new WP_Query( 2018 array( 2019 'meta_query' => array( 2020 array( 2021 'compare_key' => 'NOT IN', 2022 'key' => array( 'aaa_foo_bbb', 'aaa_foo_ddd' ), 2023 ), 2024 ), 2025 'fields' => 'ids', 2026 ) 2027 ); 2028 2029 $this->assertEqualSets( array( $posts[1] ), $q->posts ); 2030 } 2031 2032 /** 2033 * @ticket 43446 2034 */ 2035 public function test_compare_key_not_exists() { 2036 $posts = self::factory()->post->create_many( 3 ); 2037 2038 add_post_meta( $posts[0], 'aaa_foo_aaa', 'abc' ); 2039 add_post_meta( $posts[1], 'aaa_bar_aaa', 'abc' ); 2040 add_post_meta( $posts[2], 'aaa_foo_bbb', 'abc' ); 2041 add_post_meta( $posts[2], 'aaa_foo_ccc', 'abc' ); 2042 2043 $q = new WP_Query( 2044 array( 2045 'meta_query' => array( 2046 array( 2047 'compare_key' => 'NOT EXISTS', 2048 'key' => 'aaa_foo_bbb', 2049 'value' => 'abc', 2050 ), 2051 ), 2052 'fields' => 'ids', 2053 ) 2054 ); 2055 2056 $this->assertEqualSets( array( $posts[0], $posts[1] ), $q->posts ); 2057 } 2058 2059 /** 2060 * @ticket 43446 2061 */ 2062 public function test_compare_key_exists() { 2063 $posts = self::factory()->post->create_many( 3 ); 2064 2065 add_post_meta( $posts[0], 'aaa_foo_aaa', 'abc' ); 2066 add_post_meta( $posts[1], 'aaa_bar_aaa', 'abc' ); 2067 add_post_meta( $posts[2], 'aaa_foo_bbb', 'abc' ); 2068 add_post_meta( $posts[2], 'aaa_foo_ccc', 'abc' ); 2069 2070 $q = new WP_Query( 2071 array( 2072 'meta_query' => array( 2073 array( 2074 'compare_key' => 'EXISTS', 2075 'key' => 'aaa_foo_bbb', 2076 'value' => 'abc', 2077 ), 2078 ), 2079 'fields' => 'ids', 2080 ) 2081 ); 2082 2083 $this->assertEqualSets( array( $posts[2] ), $q->posts ); 2084 } 2085 2086 /** 2087 * @ticket 43446 2088 */ 2089 public function test_compare_key_regexp_rlike() { 2090 $posts = self::factory()->post->create_many( 3 ); 2091 2092 add_post_meta( $posts[0], 'AAA_FOO_AAA', 'abc' ); 2093 add_post_meta( $posts[1], 'aaa_bar_aaa', 'abc' ); 2094 add_post_meta( $posts[2], 'aaa_foo_bbb', 'abc' ); 2095 add_post_meta( $posts[2], 'aaa_foo_aaa', 'abc' ); 2096 2097 $q = new WP_Query( 2098 array( 2099 'meta_query' => array( 2100 array( 2101 'compare_key' => 'REGEXP', 2102 'key' => 'AAA_foo_.*', 2103 ), 2104 ), 2105 'fields' => 'ids', 2106 ) 2107 ); 2108 2109 $this->assertEqualSets( array( $posts[0], $posts[2] ), $q->posts ); 2110 2111 $q = new WP_Query( 2112 array( 2113 'meta_query' => array( 2114 array( 2115 'compare_key' => 'RLIKE', 2116 'key' => 'AAA_FOO_.*', 2117 'type_key' => 'BINARY', 2118 ), 2119 ), 2120 'fields' => 'ids', 2121 ) 2122 ); 2123 2124 $this->assertEqualSets( array( $posts[0] ), $q->posts ); 2125 } 2126 2127 /** 2128 * @ticket 43446 2129 */ 2130 public function test_compare_key_not_regexp() { 2131 $posts = self::factory()->post->create_many( 3 ); 2132 2133 add_post_meta( $posts[0], 'AAA_FOO_AAA', 'abc' ); 2134 add_post_meta( $posts[0], 'AAA_foo_AAA', 'abc' ); 2135 add_post_meta( $posts[1], 'aaa_bar_aaa', 'abc' ); 2136 add_post_meta( $posts[2], 'aaa_foo_bbb', 'abc' ); 2137 add_post_meta( $posts[2], 'aaa_foo_aaa', 'abc' ); 2138 2139 $q = new WP_Query( 2140 array( 2141 'meta_query' => array( 2142 array( 2143 'compare_key' => 'NOT REGEXP', 2144 'key' => 'AAA_foo_.*', 2145 ), 2146 ), 2147 'fields' => 'ids', 2148 ) 2149 ); 2150 2151 $this->assertEqualSets( array( $posts[1] ), $q->posts ); 2152 2153 $q = new WP_Query( 2154 array( 2155 'meta_query' => array( 2156 array( 2157 'compare_key' => 'NOT REGEXP', 2158 'key' => 'AAA_FOO_.*', 2159 'type_key' => 'BINARY', 2160 ), 2161 ), 2162 'fields' => 'ids', 2163 ) 2164 ); 2165 2166 $this->assertEqualSets( array( $posts[1], $posts[2] ), $q->posts ); 2167 } 1925 2168 }
Note: See TracChangeset
for help on using the changeset viewer.