Make WordPress Core

Changeset 60917


Ignore:
Timestamp:
10/09/2025 05:21:12 AM (10 months ago)
Author:
ramonopoly
Message:

Attachments REST API endpoint: add support for filtering attachments by multiple media types

This patch enhances the REST API media endpoint to allow filtering by multiple values for the media_type and mime_type parameters. String, comma-separated values and array are supported.

Props abcd95, ramonopoly, andrewserong, mukesh27, adamsilverstein, timothyblynjacobs, swissspidy.

Fixes #63668.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php

    r60916 r60917  
    7171         *
    7272         * @since 4.7.0
     73         * @since 6.9.0 Extends the `media_type` and `mime_type` request arguments to support array values.
    7374         *
    7475         * @param array           $prepared_args Optional. Array of prepared arguments. Default empty array.
     
    8384                }
    8485
    85                 $media_types = $this->get_media_types();
    86 
    87                 if ( ! empty( $request['media_type'] ) && isset( $media_types[ $request['media_type'] ] ) ) {
    88                         $query_args['post_mime_type'] = $media_types[ $request['media_type'] ];
    89                 }
    90 
    91                 if ( ! empty( $request['mime_type'] ) ) {
    92                         $parts = explode( '/', $request['mime_type'] );
    93                         if ( isset( $media_types[ $parts[0] ] ) && in_array( $request['mime_type'], $media_types[ $parts[0] ], true ) ) {
    94                                 $query_args['post_mime_type'] = $request['mime_type'];
    95                         }
     86                $all_mime_types = array();
     87                $media_types    = $this->get_media_types();
     88
     89                if ( ! empty( $request['media_type'] ) && is_array( $request['media_type'] ) ) {
     90                        foreach ( $request['media_type'] as $type ) {
     91                                if ( isset( $media_types[ $type ] ) ) {
     92                                        $all_mime_types = array_merge( $all_mime_types, $media_types[ $type ] );
     93                                }
     94                        }
     95                }
     96
     97                if ( ! empty( $request['mime_type'] ) && is_array( $request['mime_type'] ) ) {
     98                        foreach ( $request['mime_type'] as $mime_type ) {
     99                                $parts = explode( '/', $mime_type );
     100                                if ( isset( $media_types[ $parts[0] ] ) && in_array( $mime_type, $media_types[ $parts[0] ], true ) ) {
     101                                        $all_mime_types[] = $mime_type;
     102                                }
     103                        }
     104                }
     105
     106                if ( ! empty( $all_mime_types ) ) {
     107                        $query_args['post_mime_type'] = array_values( array_unique( $all_mime_types ) );
    96108                }
    97109
     
    13431355         *
    13441356         * @since 4.7.0
     1357         * @since 6.9.0 Extends the `media_type` and `mime_type` request arguments to support array values.
    13451358         *
    13461359         * @return array Query parameters for the attachment collection as an array.
     
    13501363                $params['status']['default']       = 'inherit';
    13511364                $params['status']['items']['enum'] = array( 'inherit', 'private', 'trash' );
    1352                 $media_types                       = $this->get_media_types();
     1365                $media_types                       = array_keys( $this->get_media_types() );
    13531366
    13541367                $params['media_type'] = array(
    13551368                        'default'     => null,
    1356                         'description' => __( 'Limit result set to attachments of a particular media type.' ),
    1357                         'type'        => 'string',
    1358                         'enum'        => array_keys( $media_types ),
     1369                        'description' => __( 'Limit result set to attachments of a particular media type or media types.' ),
     1370                        'type'        => 'array',
     1371                        'items'       => array(
     1372                                'type' => 'string',
     1373                                'enum' => $media_types,
     1374                        ),
    13591375                );
    13601376
    13611377                $params['mime_type'] = array(
    13621378                        'default'     => null,
    1363                         'description' => __( 'Limit result set to attachments of a particular MIME type.' ),
    1364                         'type'        => 'string',
     1379                        'description' => __( 'Limit result set to attachments of a particular MIME type or MIME types.' ),
     1380                        'type'        => 'array',
     1381                        'items'       => array(
     1382                                'type' => 'string',
     1383                        ),
    13651384                );
    13661385
  • trunk/tests/phpunit/tests/rest-api/rest-attachments-controller.php

    r60916 r60917  
    3939
    4040        /**
     41         * @var string The path to the test video.
     42         */
     43        private static $test_video_file;
     44
     45        /**
     46         * @var string The path to the test audio.
     47         */
     48        private static $test_audio_file;
     49
     50        /**
     51         * @var string The path to the test RTF file.
     52         */
     53        private static $test_rtf_file;
     54
     55        /**
    4156         * @var array The recorded posts query clauses.
    4257         */
     
    86101                        unlink( self::$test_avif_file );
    87102                }
     103                if ( file_exists( self::$test_video_file ) ) {
     104                        unlink( self::$test_video_file );
     105                }
     106                if ( file_exists( self::$test_audio_file ) ) {
     107                        unlink( self::$test_audio_file );
     108                }
     109                if ( file_exists( self::$test_rtf_file ) ) {
     110                        unlink( self::$test_rtf_file );
     111                }
    88112
    89113                self::delete_user( self::$editor_id );
     
    125149                if ( ! file_exists( self::$test_svg_file ) ) {
    126150                        copy( $test_svg_file, self::$test_svg_file );
     151                }
     152
     153                $test_video_file       = DIR_TESTDATA . '/uploads/small-video.mp4';
     154                self::$test_video_file = get_temp_dir() . 'small-video.mp4';
     155                if ( ! file_exists( self::$test_video_file ) ) {
     156                        copy( $test_video_file, self::$test_video_file );
     157                }
     158
     159                $test_audio_file       = DIR_TESTDATA . '/uploads/small-audio.mp3';
     160                self::$test_audio_file = get_temp_dir() . 'small-audio.mp3';
     161                if ( ! file_exists( self::$test_audio_file ) ) {
     162                        copy( $test_audio_file, self::$test_audio_file );
     163                }
     164
     165                $test_rtf_file       = DIR_TESTDATA . '/uploads/test.rtf';
     166                self::$test_rtf_file = get_temp_dir() . 'test.rtf';
     167                if ( ! file_exists( self::$test_rtf_file ) ) {
     168                        copy( $test_rtf_file, self::$test_rtf_file );
    127169                }
    128170
     
    268310                        'text',
    269311                );
    270                 $this->assertSameSets( $media_types, $data['endpoints'][0]['args']['media_type']['enum'] );
     312                $this->assertSameSets( $media_types, $data['endpoints'][0]['args']['media_type']['items']['enum'] );
    271313        }
    272314
     
    417459                $data     = $response->get_data();
    418460                $this->assertSame( $id1, $data[0]['id'] );
     461        }
     462
     463        /**
     464         * Test multiple media types support with various input formats.
     465         *
     466         * @ticket 63668
     467         */
     468        public function test_get_items_multiple_media_types() {
     469                $image_id = self::factory()->attachment->create_object(
     470                        self::$test_file,
     471                        0,
     472                        array(
     473                                'post_mime_type' => 'image/jpeg',
     474                        )
     475                );
     476
     477                $video_id = self::factory()->attachment->create_object(
     478                        self::$test_video_file,
     479                        0,
     480                        array(
     481                                'post_mime_type' => 'video/mp4',
     482                        )
     483                );
     484
     485                $audio_id = self::factory()->attachment->create_object(
     486                        self::$test_audio_file,
     487                        0,
     488                        array(
     489                                'post_mime_type' => 'audio/mpeg',
     490                        )
     491                );
     492
     493                $request = new WP_REST_Request( 'GET', '/wp/v2/media' );
     494
     495                // Test single media type.
     496                $request->set_param( 'media_type', 'image' );
     497                $response = rest_get_server()->dispatch( $request );
     498                $data     = $response->get_data();
     499                $this->assertCount( 1, $data, 'Response count for single media type is not 1' );
     500                $this->assertSame( $image_id, $data[0]['id'], 'Image ID not found in response for single media type' );
     501
     502                // Test multiple media types with comma-separated string.
     503                $request->set_param( 'media_type', 'image,video' );
     504                $response = rest_get_server()->dispatch( $request );
     505                $data     = $response->get_data();
     506                $this->assertCount( 2, $data, 'Response count for multiple media types with comma-separated string is not 2' );
     507                $ids = wp_list_pluck( $data, 'id' );
     508                $this->assertContains( $image_id, $ids, 'Image ID not found in response for multiple media types with comma-separated string' );
     509                $this->assertContains( $video_id, $ids, 'Video ID not found in response for multiple media types with comma-separated string' );
     510                $this->assertNotContains( $audio_id, $ids, 'Audio ID found in response for multiple media types with comma-separated string' );
     511
     512                // Test multiple media types with array format.
     513                $request->set_param( 'media_type', array( 'image', 'video', 'audio' ) );
     514                $response = rest_get_server()->dispatch( $request );
     515                $data     = $response->get_data();
     516                $this->assertCount( 3, $data, 'Response count for multiple media types with array format is not 3' );
     517                $ids = wp_list_pluck( $data, 'id' );
     518                $this->assertContains( $image_id, $ids, 'Image ID not found in response for multiple media types with array format' );
     519                $this->assertContains( $video_id, $ids, 'Video ID not found in response for multiple media types with array format' );
     520                $this->assertContains( $audio_id, $ids, 'Audio ID not found in response for multiple media types with array format' );
     521
     522                // Test invalid media type mixed with valid ones.
     523                $request->set_param( 'media_type', 'image,invalid,video' );
     524                $response = rest_get_server()->dispatch( $request );
     525                $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
     526        }
     527
     528        /**
     529         * Test multiple MIME types support and combination with media types.
     530         *
     531         * @ticket 63668
     532         */
     533        public function test_get_items_multiple_mime_types_and_combination() {
     534                $jpeg_id = self::factory()->attachment->create_object(
     535                        self::$test_file,
     536                        0,
     537                        array(
     538                                'post_mime_type' => 'image/jpeg',
     539                        )
     540                );
     541
     542                $png_id = self::factory()->attachment->create_object(
     543                        self::$test_file2,
     544                        0,
     545                        array(
     546                                'post_mime_type' => 'image/png',
     547                        )
     548                );
     549
     550                $mp4_id = self::factory()->attachment->create_object(
     551                        self::$test_video_file,
     552                        0,
     553                        array(
     554                                'post_mime_type' => 'video/mp4',
     555                        )
     556                );
     557
     558                $request = new WP_REST_Request( 'GET', '/wp/v2/media' );
     559
     560                // Test single MIME type
     561                $request->set_param( 'mime_type', 'image/jpeg' );
     562                $response = rest_get_server()->dispatch( $request );
     563                $data     = $response->get_data();
     564                $this->assertCount( 1, $data, 'Response count for single MIME type is not 1' );
     565                $this->assertSame( $jpeg_id, $data[0]['id'], 'JPEG ID not found in response for single MIME type' );
     566
     567                // Test multiple MIME types with comma-separated string.
     568                $request->set_param( 'mime_type', 'image/jpeg,image/png' );
     569                $response = rest_get_server()->dispatch( $request );
     570                $data     = $response->get_data();
     571                $this->assertCount( 2, $data, 'Response count for multiple MIME types with comma-separated string is not 2' );
     572                $ids = wp_list_pluck( $data, 'id' );
     573                $this->assertContains( $jpeg_id, $ids, 'JPEG ID not found in response for multiple MIME types with comma-separated string' );
     574                $this->assertContains( $png_id, $ids, 'PNG ID not found in response for multiple MIME types with comma-separated string' );
     575
     576                // Test multiple MIME types with array format.
     577                $request->set_param( 'mime_type', array( 'image/jpeg', 'video/mp4' ) );
     578                $response = rest_get_server()->dispatch( $request );
     579                $data     = $response->get_data();
     580                $this->assertCount( 2, $data, 'Response count for multiple MIME types with array format is not 2' );
     581                $ids = wp_list_pluck( $data, 'id' );
     582
     583                $this->assertContains( $jpeg_id, $ids, 'JPEG ID not found in response for multiple MIME types with array format' );
     584                $this->assertContains( $mp4_id, $ids, 'MP4 ID not found in response for multiple MIME types with array format' );
     585        }
     586
     587        /**
     588         * Test combination of media type and mime type parameters.
     589         *
     590         * @ticket 63668
     591         */
     592        public function test_get_items_with_media_type_and_media_types() {
     593                $audio_id = self::factory()->attachment->create_object(
     594                        self::$test_audio_file,
     595                        0,
     596                        array(
     597                                'post_mime_type' => 'audio/mpeg',
     598                                'post_excerpt'   => 'A sample caption',
     599                        )
     600                );
     601
     602                $jpeg_id = self::factory()->attachment->create_object(
     603                        self::$test_file,
     604                        0,
     605                        array(
     606                                'post_mime_type' => 'image/jpeg',
     607                                'post_excerpt'   => 'A sample caption',
     608                        )
     609                );
     610
     611                $png_id = self::factory()->attachment->create_object(
     612                        self::$test_file2,
     613                        0,
     614                        array(
     615                                'post_mime_type' => 'image/png',
     616                        )
     617                );
     618
     619                $video_id = self::factory()->attachment->create_object(
     620                        self::$test_video_file,
     621                        0,
     622                        array(
     623                                'post_mime_type' => 'video/mp4',
     624                        )
     625                );
     626
     627                $rtf_id = self::factory()->attachment->create_object(
     628                        self::$test_rtf_file,
     629                        0,
     630                        array(
     631                                'post_mime_type' => 'application/rtf',
     632                        )
     633                );
     634
     635                // Test combination of single media type and single mime type parameters.
     636                $request = new WP_REST_Request( 'GET', '/wp/v2/media' );
     637                $request->set_param( 'media_type', 'image' );
     638                $request->set_param( 'mime_type', 'audio/mpeg' );
     639                $response = rest_get_server()->dispatch( $request );
     640                $data     = $response->get_data();
     641                $ids      = wp_list_pluck( $data, 'id' );
     642
     643                $this->assertCount( 3, $data, 'Response count for combination of single media type and single mime type parameters is not 3' );
     644                $this->assertContains( $jpeg_id, $ids, 'JPEG ID not found in response' );
     645                $this->assertContains( $png_id, $ids, 'PNG ID not found in response' );
     646                $this->assertContains( $audio_id, $ids, 'Audio ID found in response' );
     647
     648                // Test combination of single media type and multiple mime type parameters.
     649                $request = new WP_REST_Request( 'GET', '/wp/v2/media' );
     650                $request->set_param( 'media_type', 'audio' );
     651                $request->set_param( 'mime_type', array( 'image/jpeg', 'image/png' ) );
     652                $response = rest_get_server()->dispatch( $request );
     653                $data     = $response->get_data();
     654                $ids      = wp_list_pluck( $data, 'id' );
     655
     656                $this->assertCount( 3, $data, 'Response count for combination of single media type and multiple mime type parameters is not 3' );
     657                $this->assertContains( $audio_id, $ids, 'Audio ID not found in response' );
     658                $this->assertContains( $jpeg_id, $ids, 'JPEG ID not found in response' );
     659                $this->assertContains( $png_id, $ids, 'PNG ID not found in response' );
     660
     661                // Test combination of multiple media types and single mime type parameters.
     662                $request = new WP_REST_Request( 'GET', '/wp/v2/media' );
     663                $request->set_param( 'media_type', 'audio,video' );
     664                $request->set_param( 'mime_type', array( 'image/jpeg' ) );
     665                $response = rest_get_server()->dispatch( $request );
     666                $data     = $response->get_data();
     667                $ids      = wp_list_pluck( $data, 'id' );
     668
     669                $this->assertCount( 3, $data, 'Response count for combination of multiple media type and multiple mime type parameters is not 3' );
     670                $this->assertContains( $audio_id, $ids, 'Audio ID not found in response' );
     671                $this->assertContains( $jpeg_id, $ids, 'JPEG ID not found in response' );
     672                $this->assertContains( $video_id, $ids, 'Video ID not found in response' );
     673
     674                // Test combination of multiple media types and multiple mime type parameters.
     675                $request = new WP_REST_Request( 'GET', '/wp/v2/media' );
     676                $request->set_param( 'media_type', 'audio,video' );
     677                $request->set_param( 'mime_type', array( 'image/jpeg', 'image/png', 'application/rtf' ) );
     678                $response = rest_get_server()->dispatch( $request );
     679                $data     = $response->get_data();
     680                $ids      = wp_list_pluck( $data, 'id' );
     681
     682                $this->assertCount( 5, $data, 'Response count for combination of multiple media type and multiple mime type parameters is not 3' );
     683                $this->assertContains( $audio_id, $ids, 'Audio ID not found in response' );
     684                $this->assertContains( $jpeg_id, $ids, 'JPEG ID not found in response' );
     685                $this->assertContains( $video_id, $ids, 'Video ID not found in response' );
     686                $this->assertContains( $png_id, $ids, 'PNG ID not found in response' );
     687                $this->assertContains( $rtf_id, $ids, 'RTF ID not found in response' );
    419688        }
    420689
     
    26862955        /**
    26872956         * Tests that the attachment fields caption, description, and title, post and alt_text are updated correctly.
     2957         *
    26882958         * @ticket 64035
    26892959         * @requires function imagejpeg
  • trunk/tests/qunit/fixtures/wp-api-generated.js

    r60908 r60917  
    29642964                        "media_type": {
    29652965                            "default": null,
    2966                             "description": "Limit result set to attachments of a particular media type.",
    2967                             "type": "string",
    2968                             "enum": [
    2969                                 "image",
    2970                                 "video",
    2971                                 "text",
    2972                                 "application",
    2973                                 "audio"
    2974                             ],
     2966                            "description": "Limit result set to attachments of a particular media type or media types.",
     2967                            "type": "array",
     2968                            "items": {
     2969                                "type": "string",
     2970                                "enum": [
     2971                                    "image",
     2972                                    "video",
     2973                                    "text",
     2974                                    "application",
     2975                                    "audio"
     2976                                ]
     2977                            },
    29752978                            "required": false
    29762979                        },
    29772980                        "mime_type": {
    29782981                            "default": null,
    2979                             "description": "Limit result set to attachments of a particular MIME type.",
    2980                             "type": "string",
     2982                            "description": "Limit result set to attachments of a particular MIME type or MIME types.",
     2983                            "type": "array",
     2984                            "items": {
     2985                                "type": "string"
     2986                            },
    29812987                            "required": false
    29822988                        }
Note: See TracChangeset for help on using the changeset viewer.