diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index a0d3fe805d..d0333c61c4 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -270,6 +270,10 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); + if ( ! empty( $request['tax_relation'] ) ) { + $query_args['tax_query'] = array( 'relation' => $request['tax_relation'] ); + } + foreach ( $taxonomies as $taxonomy ) { $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; $tax_exclude = $base . '_exclude'; @@ -2531,6 +2535,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); + if ( ! empty( $taxonomies ) ) { + $query_params['tax_relation'] = array( + 'description' => __( 'Limit result set based on relationship between multiple taxonomies.' ), + 'type' => 'string', + 'enum' => array( 'AND', 'OR' ), + ); + } + foreach ( $taxonomies as $taxonomy ) { $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index 37ee1ac02c..ed95e65a89 100644 --- a/tests/phpunit/tests/rest-api/rest-posts-controller.php +++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php @@ -175,6 +175,7 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 'sticky', 'tags', 'tags_exclude', + 'tax_relation', ), $keys ); @@ -959,6 +960,32 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); } + /** + * @ticket 44326 + */ + public function test_get_items_tags_or_categories_query() { + $id1 = self::$post_id; + $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id3 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id4 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $tag = wp_insert_term( 'My Tag', 'post_tag' ); + $category = wp_insert_term( 'My Category', 'category' ); + + wp_set_object_terms( $id1, array( $tag['term_id'] ), 'post_tag' ); + wp_set_object_terms( $id2, array( $category['term_id'] ), 'category' ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'tax_relation', 'OR' ); + $request->set_param( 'tags', array( $tag['term_id'] ) ); + $request->set_param( 'categories', array( $category['term_id'] ) ); + + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertCount( 2, $data ); + $this->assertEquals( $id2, $data[0]['id'] ); + $this->assertEquals( $id1, $data[1]['id'] ); + } + public function test_get_items_tags_and_categories_exclude_query() { $id1 = self::$post_id; $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); @@ -985,6 +1012,49 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); } + /** + * @ticket 44326 + */ + public function test_get_items_tags_or_categories_exclude_query() { + $id1 = self::$post_id; + $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id3 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id4 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $tag = wp_insert_term( 'My Tag', 'post_tag' ); + $category = wp_insert_term( 'My Category', 'category' ); + + wp_set_object_terms( $id1, array( $tag['term_id'] ), 'post_tag' ); + wp_set_object_terms( $id2, array( $tag['term_id'] ), 'post_tag' ); + wp_set_object_terms( $id2, array( $category['term_id'] ), 'category' ); + wp_set_object_terms( $id3, array( $category['term_id'] ), 'category' ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'tags', array( $tag['term_id'] ) ); + $request->set_param( 'categories_exclude', array( $category['term_id'] ) ); + $request->set_param( 'tax_relation', 'OR' ); + + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertCount( 3, $data ); + $this->assertEquals( $id2, $data[0]['id'] ); + $this->assertEquals( $id4, $data[1]['id'] ); + $this->assertEquals( $id1, $data[2]['id'] ); + } + + /** + * @ticket 44326 + */ + public function test_get_items_relation_with_no_tax_query() { + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'tax_relation', 'OR' ); + $request->set_param( 'include', self::$post_id ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $this->assertCount( 1, $response->get_data() ); + $this->assertEquals( self::$post_id, $response->get_data()[0]['id'] ); + } + public function test_get_items_sticky() { $id1 = self::$post_id; $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 816b5d53eb..de04847b50 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -322,6 +322,15 @@ mockedApiResponse.Schema = { "type": "string" } }, + "tax_relation": { + "required": false, + "enum": [ + "AND", + "OR" + ], + "description": "Limit result set based on relationship between multiple taxonomies.", + "type": "string" + }, "categories": { "required": false, "default": [],