Make WordPress Core

Changeset 60917


Ignore:
Timestamp:
10/09/2025 05:21:12 AM (7 weeks 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.