From 9b550e2bf3e78f2d35ebf798278e08ee84f21b2f Mon Sep 17 00:00:00 2001 From: Ramon Date: Fri, 3 Oct 2025 16:04:02 +1000 Subject: [PATCH 1/7] REST API: Extend `orderby` parameter to support `mime_type` for attachments. This update allows the `orderby` request argument in the attachments controller to include `mime_type`, enhancing the sorting capabilities of media items. Additionally, a new test has been added to verify the correct functionality of this feature. Fixes trac 64073. --- .../class-wp-rest-attachments-controller.php | 3 ++ .../rest-api/rest-attachments-controller.php | 43 +++++++++++++++++++ tests/qunit/fixtures/wp-api-generated.js | 3 +- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index faeb01c93904a..8b8115848e944 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -1355,6 +1355,7 @@ public static function get_filename_from_disposition( $disposition_header ) { * * @since 4.7.0 * @since 6.9.0 Extends the `media_type` and `mime_type` request arguments to support array values. + * @since 6.9.0 Extends the `orderby` request argument to support `mime_type`. * * @return array Query parameters for the attachment collection as an array. */ @@ -1383,6 +1384,8 @@ public function get_collection_params() { ), ); + $params['orderby']['enum'][] = 'mime_type'; + return $params; } diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index bc1ca41b578ee..988307c80d5df 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -3097,4 +3097,47 @@ public function test_edit_image_vertical_flip_only() { // The controller converts the integer values to booleans: 0 !== (int) 1 = true. $this->assertSame( array( true, false ), WP_Image_Editor_Mock::$spy['flip'][0], 'Vertical flip of the image is not identical.' ); } + + /** + * Test that the `orderby` parameter works with the `mime_type` parameter. + * + * @ticket 64073 + */ + public function test_get_items_orderby_mime_type() { + $jpeg_id = self::factory()->attachment->create_object( + self::$test_file, + 0, + array( + 'post_mime_type' => 'image/jpeg', + 'post_excerpt' => 'A sample caption', + ) + ); + + $png_id = self::factory()->attachment->create_object( + self::$test_file2, + 0, + array( + 'post_mime_type' => 'image/png', + 'post_excerpt' => 'A sample caption', + ) + ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/media' ); + + // Check ordering. Default ORDER is DESC. + $request->set_param( 'orderby', 'mime_type' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertCount( 2, $data, 'Response count for orderby DESC mime_type is not 2' ); + $this->assertSame( $png_id, $data[0]['id'], 'PNG ID not found in response for orderby DESC mime_type' ); + $this->assertSame( $jpeg_id, $data[1]['id'], 'JPEG ID not found in response for orderby DESC mime_type' ); + + // ASC order. + $request->set_param( 'order', 'asc' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertCount( 2, $data, 'Response count for orderby ASC mime_type is not 2' ); + $this->assertSame( $jpeg_id, $data[0]['id'], 'JPEG ID not found in response for orderby ASC mime_type' ); + $this->assertSame( $png_id, $data[1]['id'], 'PNG ID not found in response for orderby ASC mime_type' ); + } } diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index bc9ea0a2dc424..fceb635aca10c 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -2903,7 +2903,8 @@ mockedApiResponse.Schema = { "relevance", "slug", "include_slugs", - "title" + "title", + "mime_type" ], "required": false }, From 0364466a9478f8a3a82c358cf298746c94f00941 Mon Sep 17 00:00:00 2001 From: Ramon Date: Fri, 3 Oct 2025 16:40:10 +1000 Subject: [PATCH 2/7] Updates WP_Query::parse_orderby to allow post_mime_type --- src/wp-includes/class-wp-query.php | 3 ++ .../class-wp-rest-attachments-controller.php | 12 +++++++ .../rest-api/rest-attachments-controller.php | 34 +++++++++++++++---- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index 122764d6f9bb2..3082d49a2e9d2 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -1679,6 +1679,7 @@ protected function parse_search_order( &$query_vars ) { * Converts the given orderby alias (if allowed) to a properly-prefixed value. * * @since 4.0.0 + * @since 6.9.0 Extends allowed_keys to support ordering by `post_mime_type`. * * @global wpdb $wpdb WordPress database abstraction object. * @@ -1695,6 +1696,7 @@ protected function parse_orderby( $orderby ) { 'post_date', 'post_title', 'post_modified', + 'post_mime_type', 'post_parent', 'post_type', 'name', @@ -1748,6 +1750,7 @@ protected function parse_orderby( $orderby ) { case 'post_author': case 'post_date': case 'post_title': + case 'post_mime_type': case 'post_modified': case 'post_parent': case 'post_type': diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index 8b8115848e944..b3c9605f29270 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -71,6 +71,7 @@ public function register_routes() { * * @since 4.7.0 * @since 6.9.0 Extends the `media_type` and `mime_type` request arguments to support array values. + * @since 6.9.0 Extends the `orderby` request argument to support `mime_type`. * * @param array $prepared_args Optional. Array of prepared arguments. Default empty array. * @param WP_REST_Request $request Optional. Request to prepare items for. @@ -112,6 +113,17 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' ); } + // Map to proper WP_Query orderby param - this needs to happen AFTER parent class + if ( isset( $query_args['orderby'] ) && isset( $request['orderby'] ) ) { + $orderby_mappings = array( + 'mime_type' => 'post_mime_type', + ); + + if ( isset( $orderby_mappings[ $request['orderby'] ] ) ) { + $query_args['orderby'] = $orderby_mappings[ $request['orderby'] ]; + } + } + return $query_args; } diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index 988307c80d5df..f8c32c85d4b15 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -3122,22 +3122,44 @@ public function test_get_items_orderby_mime_type() { ) ); + $avif_id = self::factory()->attachment->create_object( + self::$test_avif_file, + 0, + array( + 'post_mime_type' => 'video/avif', + 'post_excerpt' => 'A sample caption', + ) + ); + + $svg_id = self::factory()->attachment->create_object( + self::$test_svg_file, + 0, + array( + 'post_mime_type' => 'image/svg+xml', + 'post_excerpt' => 'A sample caption', + ) + ); + $request = new WP_REST_Request( 'GET', '/wp/v2/media' ); + $request->set_param( '_fields', 'id,mime_type' ); // Check ordering. Default ORDER is DESC. $request->set_param( 'orderby', 'mime_type' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); - $this->assertCount( 2, $data, 'Response count for orderby DESC mime_type is not 2' ); - $this->assertSame( $png_id, $data[0]['id'], 'PNG ID not found in response for orderby DESC mime_type' ); - $this->assertSame( $jpeg_id, $data[1]['id'], 'JPEG ID not found in response for orderby DESC mime_type' ); + $this->assertCount( 4, $data, 'Response count for orderby DESC mime_type is not 4' ); + // Check that ordering is working by verifying the mime types are in order + $mime_types = array_column( $data, 'mime_type' ); + $expected_desc = array( 'video/avif', 'image/svg+xml', 'image/png', 'image/jpeg' ); + $this->assertSame( $expected_desc, $mime_types, 'MIME types not in expected DESC order' ); // ASC order. $request->set_param( 'order', 'asc' ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); - $this->assertCount( 2, $data, 'Response count for orderby ASC mime_type is not 2' ); - $this->assertSame( $jpeg_id, $data[0]['id'], 'JPEG ID not found in response for orderby ASC mime_type' ); - $this->assertSame( $png_id, $data[1]['id'], 'PNG ID not found in response for orderby ASC mime_type' ); + + $mime_types = array_column( $data, 'mime_type' ); + $expected_asc = array( 'image/jpeg', 'image/png', 'image/svg+xml', 'video/avif' ); + $this->assertSame( $expected_asc, $mime_types, 'MIME types not in expected ASC order' ); } } From 826a212b08bf58dbb509829c7dcc7df517ef50e2 Mon Sep 17 00:00:00 2001 From: Ramon Date: Sat, 4 Oct 2025 09:19:48 +1000 Subject: [PATCH 3/7] Apply suggestion from @TimothyBJacobs combine isset Co-authored-by: Timothy Jacobs --- .../rest-api/endpoints/class-wp-rest-attachments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index b3c9605f29270..e39102ba381f8 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -114,7 +114,7 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul } // Map to proper WP_Query orderby param - this needs to happen AFTER parent class - if ( isset( $query_args['orderby'] ) && isset( $request['orderby'] ) ) { + if ( isset( $query_args['orderby'], $request['orderby'] ) ) { $orderby_mappings = array( 'mime_type' => 'post_mime_type', ); From 9fa60d4f4b536dc836fcd231330b8ce35efa9fc4 Mon Sep 17 00:00:00 2001 From: Ramon Date: Sat, 4 Oct 2025 09:20:02 +1000 Subject: [PATCH 4/7] Apply suggestion from @mukeshpanchal27 period Co-authored-by: Mukesh Panchal --- .../rest-api/endpoints/class-wp-rest-attachments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index e39102ba381f8..07b5d4700138d 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -113,7 +113,7 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' ); } - // Map to proper WP_Query orderby param - this needs to happen AFTER parent class + // Map to proper WP_Query orderby param - this needs to happen AFTER parent class. if ( isset( $query_args['orderby'], $request['orderby'] ) ) { $orderby_mappings = array( 'mime_type' => 'post_mime_type', From acbd30e0f307155a5ddb772d9048b6619a3a99cf Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 9 Oct 2025 11:36:09 +1100 Subject: [PATCH 5/7] Update src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php Co-authored-by: Andrew Serong <14988353+andrewserong@users.noreply.github.com> --- .../rest-api/endpoints/class-wp-rest-attachments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index 07b5d4700138d..e25e415a25b66 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -113,7 +113,7 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' ); } - // Map to proper WP_Query orderby param - this needs to happen AFTER parent class. + // Map to proper WP_Query orderby param. if ( isset( $query_args['orderby'], $request['orderby'] ) ) { $orderby_mappings = array( 'mime_type' => 'post_mime_type', From 5d916352ad3e1f3156a067d463a6a65f3e8b04cd Mon Sep 17 00:00:00 2001 From: Ramon Date: Fri, 10 Oct 2025 17:08:58 +1100 Subject: [PATCH 6/7] Add tests for attachment ordering by post_mime_type --- tests/phpunit/tests/query/results.php | 91 +++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/tests/phpunit/tests/query/results.php b/tests/phpunit/tests/query/results.php index 412d02fe83be6..48a390b75b7e4 100644 --- a/tests/phpunit/tests/query/results.php +++ b/tests/phpunit/tests/query/results.php @@ -20,6 +20,11 @@ class Tests_Query_Results extends WP_UnitTestCase { public static $child_two; public static $child_three; public static $child_four; + public static $image1_jpg; + public static $image2_jpg; + public static $image3_png; + public static $audio1_mp3; + public static $video1_mp4; public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { $cat_a = $factory->term->create( @@ -292,6 +297,47 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { ) ); self::$post_ids[] = self::$child_four; + + self::$image1_jpg = $factory->attachment->create_object( + 'image1.jpg', + 0, + array( + 'post_mime_type' => 'image/jpeg', + 'post_date' => '2025-09-29 06:22:49', + ) + ); + self::$image2_jpg = $factory->attachment->create_object( + 'image2.jpg', + 0, + array( + 'post_mime_type' => 'image/jpeg', + 'post_date' => '2025-09-29 06:22:49', + ) + ); + self::$image3_png = $factory->attachment->create_object( + 'image3.png', + 0, + array( + 'post_mime_type' => 'image/png', + 'post_date' => '2025-09-29 06:22:49', + ) + ); + self::$audio1_mp3 = $factory->attachment->create_object( + 'audio1.mp3', + 0, + array( + 'post_mime_type' => 'audio/mpeg', + 'post_date' => '2025-09-29 06:22:49', + ) + ); + self::$video1_mp4 = $factory->attachment->create_object( + 'video1.mp4', + 0, + array( + 'post_mime_type' => 'video/mp4', + 'post_date' => '2025-09-29 06:22:49', + ) + ); } public function set_up() { @@ -1264,4 +1310,49 @@ public function test_main_comments_feed_includes_attachment_comments() { $feed_comment = $this->q->next_comment(); $this->assertEquals( $comment_id, $feed_comment->comment_ID ); } + + /** + * @ticket 64073 + */ + public function test_attachment_orderby_post_mime_type() { + $all_attachments_desc = $this->q->query( + array( + 'post_type' => 'attachment', + 'orderby' => 'post_mime_type', + 'post_status' => 'inherit', + 'order' => 'desc', + ) + ); + + $this->assertCount( 5, $all_attachments_desc ); + + $this->assertSame( array( + self::$video1_mp4, + self::$image3_png, + self::$image1_jpg, + self::$image2_jpg, + self::$audio1_mp3 ), + wp_list_pluck( $all_attachments_desc, 'ID' ), + 'Order by post_mime_type order desc returns incorrect order' + ); + + $all_attachments_asc = $this->q->query( + array( + 'post_type' => 'attachment', + 'orderby' => 'post_mime_type', + 'post_status' => 'inherit', + 'order' => 'asc', + ) + ); + + $this->assertSame( array( + self::$audio1_mp3, + self::$image1_jpg, + self::$image2_jpg, + self::$image3_png, + self::$video1_mp4 ), + wp_list_pluck( $all_attachments_asc, 'ID' ), + 'Order by post_mime_type order asc returns incorrect order' + ); + } } From fb97aa1b9ee8a38216ff45d524cda4a2566d21d2 Mon Sep 17 00:00:00 2001 From: Ramon Date: Fri, 10 Oct 2025 17:10:44 +1100 Subject: [PATCH 7/7] lint --- tests/phpunit/tests/query/results.php | 30 +++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/phpunit/tests/query/results.php b/tests/phpunit/tests/query/results.php index 48a390b75b7e4..abd2b7abb13d9 100644 --- a/tests/phpunit/tests/query/results.php +++ b/tests/phpunit/tests/query/results.php @@ -1323,15 +1323,17 @@ public function test_attachment_orderby_post_mime_type() { 'order' => 'desc', ) ); - + $this->assertCount( 5, $all_attachments_desc ); - $this->assertSame( array( - self::$video1_mp4, - self::$image3_png, - self::$image1_jpg, - self::$image2_jpg, - self::$audio1_mp3 ), + $this->assertSame( + array( + self::$video1_mp4, + self::$image3_png, + self::$image1_jpg, + self::$image2_jpg, + self::$audio1_mp3, + ), wp_list_pluck( $all_attachments_desc, 'ID' ), 'Order by post_mime_type order desc returns incorrect order' ); @@ -1345,12 +1347,14 @@ public function test_attachment_orderby_post_mime_type() { ) ); - $this->assertSame( array( - self::$audio1_mp3, - self::$image1_jpg, - self::$image2_jpg, - self::$image3_png, - self::$video1_mp4 ), + $this->assertSame( + array( + self::$audio1_mp3, + self::$image1_jpg, + self::$image2_jpg, + self::$image3_png, + self::$video1_mp4, + ), wp_list_pluck( $all_attachments_asc, 'ID' ), 'Order by post_mime_type order asc returns incorrect order' );