diff --git a/includes/query/class-tmdo-post-query-router.php b/includes/query/class-tmdo-post-query-router.php index 623585a..21839d9 100644 --- a/includes/query/class-tmdo-post-query-router.php +++ b/includes/query/class-tmdo-post-query-router.php @@ -191,6 +191,25 @@ class TMDO_Post_Query_Router { $routed[ $pt ][] = $route; $sort = self::sort_for( $pt, $route, $direction ); } + // meta_key alone (no meta_value, not a meta_value sort): WP_Query adds a + // clause that tests the key exists — HivePress's featured-listing lookup. + $exists_key = ! $take_key && is_string( $meta_key ) && '' !== $meta_key + && '' === (string) $query->get( 'meta_value' ) && '' === (string) $query->get( 'meta_compare' ) + && ! in_array( $sort_name, array( 'meta_value', 'meta_value_num' ), true ) + && null !== self::field_for( array( 'key' => $meta_key ), $pt ); + if ( $exists_key ) { + $route = self::route_clause( + array( + 'key' => $meta_key, + 'compare' => 'EXISTS', + ), + self::field_for( array( 'key' => $meta_key ), $pt ) + ); + if ( ! $route ) { + return; + } + $routed[ $pt ][] = $route; + } foreach ( $raw_meta_query as $k => $clause ) { if ( 'relation' === $k || ! is_array( $clause ) ) { @@ -199,7 +218,10 @@ class TMDO_Post_Query_Router { } if ( ! isset( $clause['key'] ) ) { // Sort shape: matches every post, only orders by the key. - $name = 'AND' === $relation ? self::sort_shape( $clause ) : ''; + $name = 'AND' === $relation ? self::sort_shape( $clause ) : ''; + if ( '' !== $name && ! in_array( $name, $named, true ) && self::field_for( $clause[ $name ], $pt ) ) { + continue; // Matches every post and orders nothing here (a copy of a search's meta query). + } $field = '' !== $name && $name === $sort_name ? self::field_for( $clause[ $name ], $pt ) : null; if ( $field ) { $route = self::route_clause( array( 'compare' => 'EXISTS' ) + $clause[ $name ], $field ); @@ -239,7 +261,7 @@ class TMDO_Post_Query_Router { if ( empty( $routed ) && empty( $sort ) ) { return; } - if ( $take_key ) { + if ( $take_key || $exists_key ) { $query->set( 'meta_key', '' ); } if ( $sort ) { diff --git a/tests/integration/PostQueryRouterTest.php b/tests/integration/PostQueryRouterTest.php index 83f94ac..7286a68 100644 --- a/tests/integration/PostQueryRouterTest.php +++ b/tests/integration/PostQueryRouterTest.php @@ -436,4 +436,47 @@ class PostQueryRouterTest extends TestCase { $this->assertEmpty( $query->get( 'meta_query' ), 'both clauses routed' ); $this->assertStringContainsString( '`_stock` IS NOT NULL', $router->posts_where( '', $query ) ); } + + /** + * HivePress's featured-listing lookup (Attribute::set_search_query()): the + * search's meta query, including its sort group, plus meta_key hp_featured, + * ordered at random. The sort group (key NOT EXISTS OR key EXISTS) matches + * every post, so unused for ordering it is dropped; meta_key without a + * value or a meta_value sort tests that the key exists (2.4 PR-122). + */ + public function test_hivepress_featured_lookup_is_routed(): void { + $this->set_post_mode( 'aeav_only' ); + $router = new WPDO_Post_Query_Router(); + $query = $this->make_query( + array( + 'post_type' => 'product', + 'meta_key' => '_stock', + 'orderby' => 'rand', + 'meta_query' => array( + array( + 'relation' => 'OR', + 'price__order' => array( 'key' => '_price', 'compare' => 'NOT EXISTS', 'type' => 'DECIMAL' ), + array( 'key' => '_price', 'compare' => 'EXISTS', 'type' => 'DECIMAL' ), + ), + array( 'key' => '_price', 'value' => array( 10, 20 ), 'compare' => 'BETWEEN', 'type' => 'NUMERIC' ), + ), + ) + ); + $router->pre_get_posts( $query ); + + $this->assertSame( '', $query->get( 'meta_key' ), 'meta_key routed' ); + $this->assertEmpty( array_filter( (array) $query->get( 'meta_query' ), 'is_array' ), 'sort group dropped, filter routed' ); + $where = $router->posts_where( '', $query ); + $this->assertStringContainsString( '`_stock` IS NOT NULL', $where ); + $this->assertStringContainsString( 'BETWEEN', $where ); + } + + public function test_meta_key_with_a_value_is_left_alone(): void { + $this->set_post_mode( 'aeav_only' ); + $router = new WPDO_Post_Query_Router(); + $query = $this->make_query( array( 'post_type' => 'product', 'meta_key' => '_stock', 'meta_value' => '5' ) ); + $router->pre_get_posts( $query ); + + $this->assertSame( '_stock', $query->get( 'meta_key' ) ); + } }