REST API: Support ordering response collection by listed slugs.

Adds an "include_slug" orderby value for REST API collections to permit returning a collection filtered by slugs in the same order in which those slugs are specified.
Previously, the order of slugs provided with the ?slug query parameter had no effect on the order of the returned records.

Props wonderboymusic, ocean90, boonebgorges.
Fixes #40826.



git-svn-id: https://develop.svn.wordpress.org/trunk@41760 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
K. Adam White 2017-10-05 00:36:43 +00:00
parent 75ce48ef70
commit 8cda3a2f55
9 changed files with 98 additions and 4 deletions

View File

@ -87,6 +87,7 @@ class WP_Term_Query {
* @since 4.6.0
* @since 4.6.0 Introduced 'term_taxonomy_id' parameter.
* @since 4.7.0 Introduced 'object_ids' parameter.
* @since 4.9.0 Added 'slug__in' support for 'orderby'.
*
* @param string|array $query {
* Optional. Array or query string of term query parameters. Default empty.
@ -98,7 +99,8 @@ class WP_Term_Query {
* @type string $orderby Field(s) to order terms by. Accepts term fields ('name',
* 'slug', 'term_group', 'term_id', 'id', 'description', 'parent'),
* 'count' for term taxonomy count, 'include' to match the
* 'order' of the $include param, 'meta_value', 'meta_value_num',
* 'order' of the $include param, 'slug__in' to match the
* 'order' of the $slug param, 'meta_value', 'meta_value_num',
* the value of `$meta_key`, the array keys of `$meta_query`, or
* 'none' to omit the ORDER BY clause. Defaults to 'name'.
* @type string $order Whether to order terms in ascending or descending order.
@ -841,6 +843,9 @@ class WP_Term_Query {
} elseif ( 'include' == $_orderby && ! empty( $this->query_vars['include'] ) ) {
$include = implode( ',', wp_parse_id_list( $this->query_vars['include'] ) );
$orderby = "FIELD( t.term_id, $include )";
} elseif ( 'slug__in' == $_orderby && ! empty( $this->query_vars['slug'] ) && is_array( $this->query_vars['slug'] ) ) {
$slugs = implode( "', '", array_map( 'sanitize_title_for_query', $this->query_vars['slug__in'] ) );
$orderby = "FIELD( t.slug, '" . $slugs . "')";
} elseif ( 'none' == $_orderby ) {
$orderby = '';
} elseif ( empty( $_orderby ) || 'id' == $_orderby || 'term_id' === $_orderby ) {

View File

@ -874,9 +874,10 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
// Map to proper WP_Query orderby param.
if ( isset( $query_args['orderby'] ) && isset( $request['orderby'] ) ) {
$orderby_mappings = array(
'id' => 'ID',
'include' => 'post__in',
'slug' => 'post_name',
'id' => 'ID',
'include' => 'post__in',
'slug' => 'post_name',
'include_slugs' => 'post_name__in',
);
if ( isset( $orderby_mappings[ $request['orderby'] ] ) ) {
@ -2109,6 +2110,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
'parent',
'relevance',
'slug',
'include_slugs',
'title',
),
);

View File

@ -188,6 +188,16 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
}
}
if ( isset( $prepared_args['orderby'] ) && isset( $request['orderby'] ) ) {
$orderby_mappings = array(
'include_slugs' => 'slug__in',
);
if ( isset( $orderby_mappings[ $request['orderby'] ] ) ) {
$prepared_args['orderby'] = $orderby_mappings[ $request['orderby'] ];
}
}
if ( isset( $registered['offset'] ) && ! empty( $request['offset'] ) ) {
$prepared_args['offset'] = $request['offset'];
} else {
@ -932,6 +942,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller {
'include',
'name',
'slug',
'include_slugs',
'term_group',
'description',
'count',

View File

@ -243,6 +243,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
'name' => 'display_name',
'registered_date' => 'registered',
'slug' => 'user_nicename',
'include_slugs' => 'nicename__in',
'email' => 'user_email',
'url' => 'user_url',
);
@ -1338,6 +1339,7 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
'name',
'registered_date',
'slug',
'include_slugs',
'email',
'url',
),

View File

@ -287,6 +287,22 @@ class WP_Test_REST_Categories_Controller extends WP_Test_REST_Controller_Testcas
$this->assertEquals( 'Cantaloupe', $data[2]['name'] );
}
public function test_get_items_orderby_slugs() {
$this->factory->category->create( array( 'name' => 'Burrito' ) );
$this->factory->category->create( array( 'name' => 'Taco' ) );
$this->factory->category->create( array( 'name' => 'Chalupa' ) );
$request = new WP_REST_Request( 'GET', '/wp/v2/categories' );
$request->set_param( 'orderby', 'include_slugs' );
$request->set_param( 'slug', array( 'taco', 'burrito', 'chalupa' ) );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 'taco', $data[0]['slug'] );
$this->assertEquals( 'burrito', $data[1]['slug'] );
$this->assertEquals( 'chalupa', $data[2]['slug'] );
}
protected function post_with_categories() {
$post_id = $this->factory->post->create();
$category1 = $this->factory->category->create( array(

View File

@ -598,6 +598,24 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te
$this->assertPostsOrderedBy( '{posts}.post_name DESC' );
}
public function test_get_items_with_orderby_slugs() {
$slugs = array( 'burrito', 'taco', 'chalupa' );
foreach ( $slugs as $slug ) {
$this->factory->post->create( array( 'post_title' => $slug, 'post_name' => $slug, 'post_status' => 'publish' ) );
}
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
$request->set_param( 'orderby', 'include_slugs' );
$request->set_param( 'slug', array( 'taco', 'chalupa', 'burrito' ) );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 'taco', $data[0]['slug'] );
$this->assertEquals( 'chalupa', $data[1]['slug'] );
$this->assertEquals( 'burrito', $data[2]['slug'] );
}
public function test_get_items_with_orderby_relevance() {
$id1 = $this->factory->post->create( array( 'post_title' => 'Title is more relevant', 'post_content' => 'Content is', 'post_status' => 'publish' ) );
$id2 = $this->factory->post->create( array( 'post_title' => 'Title is', 'post_content' => 'Content is less relevant', 'post_status' => 'publish' ) );

View File

@ -250,6 +250,22 @@ class WP_Test_REST_Tags_Controller extends WP_Test_REST_Controller_Testcase {
$this->assertEquals( 'Cantaloupe', $data[2]['name'] );
}
public function test_get_items_orderby_slugs() {
$this->factory->tag->create( array( 'name' => 'Burrito' ) );
$this->factory->tag->create( array( 'name' => 'Taco' ) );
$this->factory->tag->create( array( 'name' => 'Chalupa' ) );
$request = new WP_REST_Request( 'GET', '/wp/v2/tags' );
$request->set_param( 'orderby', 'include_slugs' );
$request->set_param( 'slug', array( 'taco', 'burrito', 'chalupa' ) );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 'taco', $data[0]['slug'] );
$this->assertEquals( 'burrito', $data[1]['slug'] );
$this->assertEquals( 'chalupa', $data[2]['slug'] );
}
public function test_get_items_post_args() {
$post_id = $this->factory->post->create();
$tag1 = $this->factory->tag->create( array( 'name' => 'DC' ) );

View File

@ -409,6 +409,24 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase {
$this->assertEquals( $low_id, $data[0]['id'] );
}
public function test_get_items_orderby_slugs() {
wp_set_current_user( self::$user );
$this->factory->user->create( array( 'user_nicename' => 'burrito' ) );
$this->factory->user->create( array( 'user_nicename' => 'taco' ) );
$this->factory->user->create( array( 'user_nicename' => 'chalupa' ) );
$request = new WP_REST_Request( 'GET', '/wp/v2/users' );
$request->set_param( 'orderby', 'include_slugs' );
$request->set_param( 'slug', array( 'taco', 'burrito', 'chalupa' ) );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( 'taco', $data[0]['slug'] );
$this->assertEquals( 'burrito', $data[1]['slug'] );
$this->assertEquals( 'chalupa', $data[2]['slug'] );
}
public function test_get_items_orderby_email() {
wp_set_current_user( self::$user );

View File

@ -284,6 +284,7 @@ mockedApiResponse.Schema = {
"parent",
"relevance",
"slug",
"include_slugs",
"title"
],
"description": "Sort collection by object attribute.",
@ -905,6 +906,7 @@ mockedApiResponse.Schema = {
"parent",
"relevance",
"slug",
"include_slugs",
"title",
"menu_order"
],
@ -1443,6 +1445,7 @@ mockedApiResponse.Schema = {
"parent",
"relevance",
"slug",
"include_slugs",
"title"
],
"description": "Sort collection by object attribute.",
@ -2023,6 +2026,7 @@ mockedApiResponse.Schema = {
"include",
"name",
"slug",
"include_slugs",
"term_group",
"description",
"count"
@ -2266,6 +2270,7 @@ mockedApiResponse.Schema = {
"include",
"name",
"slug",
"include_slugs",
"term_group",
"description",
"count"
@ -2495,6 +2500,7 @@ mockedApiResponse.Schema = {
"name",
"registered_date",
"slug",
"include_slugs",
"email",
"url"
],