WordPress.org

Make WordPress Core

Ticket #38131: 38131.3.diff

File 38131.3.diff, 15.8 KB (added by TimothyBlynJacobs, 2 years ago)
  • src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php

    diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php
    index e04193a..b6a3746 100644
    a b abstract class WP_REST_Controller { 
    255255        }
    256256
    257257        /**
     258         * Filters a response based on the fields requested.
     259         *
     260         * @since 4.9.0
     261         *
     262         * @param array $data Response data to filter.
     263         * @param array $fields Fields to include. Can specify nested objects using array dot notation.
     264         *
     265         * @return array
     266         */
     267        public function filter_response_by_fields( $data, $fields ) {
     268
     269                if ( empty( $fields ) ) {
     270                        return $data;
     271                }
     272
     273                $flipped = array_flip( $fields );
     274                $filtered = array();
     275
     276                foreach ( $data as $key => $value ) {
     277                        $key_requested = isset( $flipped[ $key ] );
     278
     279                        if ( is_array( $value ) ) {
     280                                $nested_fields = $this->_get_nested_fields( $fields, $key );
     281
     282                                if ( $nested_fields ) {
     283                                        $filtered[ $key ] = $this->filter_response_by_fields( $value, $nested_fields );
     284                                } elseif ( $key_requested ) {
     285                                        $filtered[ $key ] = $value;
     286                                }
     287                        } elseif ( $key_requested ) {
     288                                $filtered[ $key ] = $value;
     289                        }
     290                }
     291
     292                return $filtered;
     293        }
     294
     295        /**
     296         * Filter the fields list to only include fields that are an array dot child of a given $key.
     297         *
     298         * @param array $fields All fields.
     299         * @param string $key   The key to look for.
     300         *
     301         * @return array The filtered list of fields.
     302         */
     303        private function _get_nested_fields( $fields, $key ) {
     304
     305                $nested = array();
     306
     307                foreach ( $fields as $field ) {
     308                        $sub = substr( $field, 0,strlen( $key ) + 1 );
     309
     310                        if ( $sub === $key . '.' ) {
     311                                $nested[] = substr( $field, strlen( $key ) + 1 );
     312                        }
     313                }
     314
     315                return $nested;
     316        }
     317
     318        /**
    258319         * Retrieves the item's schema, conforming to JSON Schema.
    259320         *
    260321         * @since 4.7.0
    abstract class WP_REST_Controller { 
    360421        }
    361422
    362423        /**
     424         * Retrieves the fields param.
     425         *
     426         * @since 4.9.0
     427         *
     428         * @param array $args
     429         *
     430         * @return array
     431         */
     432        public function get_fields_param( $args = array() ) {
     433
     434                $param_details = array(
     435                        'description'       => __( 'Limit the fields to include in the response.' ),
     436                        'type'              => 'string',
     437                        'sanitize_callback' => 'sanitize_key',
     438                        'validate_callback' => 'rest_validate_request_arg',
     439                );
     440
     441                $fields = $this->get_all_field_keys( $this->get_item_schema() );
     442
     443                if ( $fields ) {
     444                        $param_details['enum'] = $fields;
     445                }
     446
     447                return array_merge( $param_details, $args );
     448        }
     449
     450        /**
     451         * Recursively retrieve all the keys for fields in array dot notation.
     452         *
     453         * @param array  $schema
     454         * @param string $parent_field
     455         *
     456         * @return array
     457         */
     458        private function get_all_field_keys( $schema, $parent_field = '' ) {
     459
     460                $fields = array();
     461
     462                if ( $parent_field ) {
     463                        $fields[] = $parent_field;
     464                }
     465
     466                $type = (array) $schema['type'];
     467
     468                if ( ! in_array( 'object', $type, true ) || empty( $schema['properties'] ) ) {
     469                        return $fields;
     470                }
     471
     472                foreach ( $schema['properties'] as $property => $prop_schema ) {
     473                        $prop_field = $parent_field ? "{$parent_field}.{$property}" : $property;
     474                        $fields     = array_merge( $fields, $this->get_all_field_keys( $prop_schema, $prop_field ) );
     475                }
     476
     477                return $fields;
     478        }
     479
     480        /**
    363481         * Adds the values from additional fields to a data object.
    364482         *
    365483         * @since 4.7.0
  • src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

    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 f9de736..d68ffa6 100644
    a b class WP_REST_Posts_Controller extends WP_REST_Controller { 
    15311531                $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
    15321532                $data    = $this->add_additional_fields_to_object( $data, $request );
    15331533                $data    = $this->filter_response_by_context( $data, $context );
     1534                $data    = $this->filter_response_by_fields( $data, $request['_fields'] );
    15341535
    15351536                // Wrap the data in a response object.
    15361537                $response = rest_ensure_response( $data );
    class WP_REST_Posts_Controller extends WP_REST_Controller { 
    20232024                $query_params = parent::get_collection_params();
    20242025
    20252026                $query_params['context']['default'] = 'view';
     2027                $query_params['_fields'] = $this->get_fields_param();
    20262028
    20272029                $query_params['after'] = array(
    20282030                        'description'        => __( 'Limit response to posts published after a given ISO8601 compliant date.' ),
  • src/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php

    diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php
    index 0723022..7ef7f42 100644
    a b class WP_REST_Terms_Controller extends WP_REST_Controller { 
    712712                $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
    713713                $data    = $this->add_additional_fields_to_object( $data, $request );
    714714                $data    = $this->filter_response_by_context( $data, $context );
     715                $data    = $this->filter_response_by_fields( $data, $request['_fields'] );
    715716
    716717                $response = rest_ensure_response( $data );
    717718
  • tests/phpunit/tests/rest-api/rest-attachments-controller.php

    diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php
    index 98a079b..1d40e6f 100644
    a b class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control 
    140140                $keys = array_keys( $data['endpoints'][0]['args'] );
    141141                sort( $keys );
    142142                $this->assertEquals( array(
     143                        '_fields',
    143144                        'after',
    144145                        'author',
    145146                        'author_exclude',
  • tests/phpunit/tests/rest-api/rest-pages-controller.php

    diff --git a/tests/phpunit/tests/rest-api/rest-pages-controller.php b/tests/phpunit/tests/rest-api/rest-pages-controller.php
    index ee24f7a..bed80f8 100644
    a b class WP_Test_REST_Pages_Controller extends WP_Test_REST_Post_Type_Controller_Te 
    6565                $keys = array_keys( $data['endpoints'][0]['args'] );
    6666                sort( $keys );
    6767                $this->assertEquals( array(
     68                        '_fields',
    6869                        'after',
    6970                        'author',
    7071                        'author_exclude',
  • tests/phpunit/tests/rest-api/rest-posts-controller.php

    diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php
    index c86879c..c5d736c 100644
    a b class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 
    120120                $this->assertEquals( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] );
    121121        }
    122122
     123        public function test_fields_param() {
     124
     125                $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts' );
     126                $response = $this->server->dispatch( $request );
     127                $data = $response->get_data();
     128
     129                $fields = $data['endpoints'][0]['args']['_fields']['enum'];
     130                $expected = array(
     131                        'title',
     132                        'title.raw',
     133                        'title.rendered',
     134                        'content',
     135                        'content.raw',
     136                        'content.rendered',
     137                        'content.protected',
     138                        'date',
     139                        'date_gmt'
     140                );
     141
     142                foreach ( $expected as $field ) {
     143                        $this->assertContains( $field, $fields );
     144                }
     145        }
     146
    123147        public function test_registered_query_params() {
    124148                $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/posts' );
    125149                $response = $this->server->dispatch( $request );
    class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 
    127151                $keys = array_keys( $data['endpoints'][0]['args'] );
    128152                sort( $keys );
    129153                $this->assertEquals( array(
     154                        '_fields',
    130155                        'after',
    131156                        'author',
    132157                        'author_exclude',
    class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 
    30803105                $this->assertEquals( $expected_keys, $keys );
    30813106        }
    30823107
     3108        public function test_get_post_fields() {
     3109                wp_set_current_user( self::$editor_id );
     3110
     3111                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', self::$post_id ) );
     3112                $request->set_param( 'context', 'edit' );
     3113                $request->set_param( '_fields', array( 'title.raw', 'content', 'date', 'fake.prop.nested' ) );
     3114                $response = $this->server->dispatch( $request );
     3115                $data = $response->get_data();
     3116
     3117                $this->assertArrayHasKey( 'title', $data );
     3118                $this->assertArrayHasKey( 'raw', $data['title'] );
     3119                $this->assertArrayNotHasKey( 'rendered', $data['title'] );
     3120
     3121                $this->assertArrayHasKey( 'content', $data );
     3122                $this->assertArrayHasKey( 'raw', $data['content'] );
     3123                $this->assertArrayHasKey( 'rendered', $data['content'] );
     3124                $this->assertArrayHasKey( 'protected', $data['content'] );
     3125
     3126                $this->assertArrayHasKey( 'date', $data );
     3127                $this->assertArrayNotHasKey( 'date_gmt', $data );
     3128        }
     3129
    30833130        public function test_status_array_enum_args() {
    30843131                $request = new WP_REST_Request( 'GET', '/wp/v2' );
    30853132                $response = $this->server->dispatch( $request );
  • tests/qunit/fixtures/wp-api-generated.js

    diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js
    index 679d024..a522a27 100644
    a b mockedApiResponse.Schema = { 
    211211                            "description": "Limit results to those matching a string.",
    212212                            "type": "string"
    213213                        },
     214                        "_fields": {
     215                            "required": false,
     216                            "enum": [
     217                                "date",
     218                                "date_gmt",
     219                                "guid",
     220                                "guid.raw",
     221                                "guid.rendered",
     222                                "id",
     223                                "link",
     224                                "modified",
     225                                "modified_gmt",
     226                                "slug",
     227                                "status",
     228                                "type",
     229                                "password",
     230                                "title",
     231                                "title.raw",
     232                                "title.rendered",
     233                                "content",
     234                                "content.raw",
     235                                "content.rendered",
     236                                "content.protected",
     237                                "author",
     238                                "excerpt",
     239                                "excerpt.raw",
     240                                "excerpt.rendered",
     241                                "excerpt.protected",
     242                                "featured_media",
     243                                "comment_status",
     244                                "ping_status",
     245                                "format",
     246                                "meta",
     247                                "sticky",
     248                                "template",
     249                                "categories",
     250                                "tags"
     251                            ],
     252                            "description": "Limit the fields to include in the response.",
     253                            "type": "string"
     254                        },
    214255                        "after": {
    215256                            "required": false,
    216257                            "description": "Limit response to posts published after a given ISO8601 compliant date.",
    mockedApiResponse.Schema = { 
    827868                            "description": "Limit results to those matching a string.",
    828869                            "type": "string"
    829870                        },
     871                        "_fields": {
     872                            "required": false,
     873                            "enum": [
     874                                "date",
     875                                "date_gmt",
     876                                "guid",
     877                                "guid.raw",
     878                                "guid.rendered",
     879                                "id",
     880                                "link",
     881                                "modified",
     882                                "modified_gmt",
     883                                "slug",
     884                                "status",
     885                                "type",
     886                                "password",
     887                                "parent",
     888                                "title",
     889                                "title.raw",
     890                                "title.rendered",
     891                                "content",
     892                                "content.raw",
     893                                "content.rendered",
     894                                "content.protected",
     895                                "author",
     896                                "excerpt",
     897                                "excerpt.raw",
     898                                "excerpt.rendered",
     899                                "excerpt.protected",
     900                                "featured_media",
     901                                "comment_status",
     902                                "ping_status",
     903                                "menu_order",
     904                                "meta",
     905                                "template"
     906                            ],
     907                            "description": "Limit the fields to include in the response.",
     908                            "type": "string"
     909                        },
    830910                        "after": {
    831911                            "required": false,
    832912                            "description": "Limit response to posts published after a given ISO8601 compliant date.",
    mockedApiResponse.Schema = { 
    13701450                            "description": "Limit results to those matching a string.",
    13711451                            "type": "string"
    13721452                        },
     1453                        "_fields": {
     1454                            "required": false,
     1455                            "enum": [
     1456                                "date",
     1457                                "date_gmt",
     1458                                "guid",
     1459                                "guid.raw",
     1460                                "guid.rendered",
     1461                                "id",
     1462                                "link",
     1463                                "modified",
     1464                                "modified_gmt",
     1465                                "slug",
     1466                                "status",
     1467                                "type",
     1468                                "title",
     1469                                "title.raw",
     1470                                "title.rendered",
     1471                                "author",
     1472                                "comment_status",
     1473                                "ping_status",
     1474                                "meta",
     1475                                "template",
     1476                                "alt_text",
     1477                                "caption",
     1478                                "caption.raw",
     1479                                "caption.rendered",
     1480                                "description",
     1481                                "description.raw",
     1482                                "description.rendered",
     1483                                "media_type",
     1484                                "mime_type",
     1485                                "media_details",
     1486                                "post",
     1487                                "source_url"
     1488                            ],
     1489                            "description": "Limit the fields to include in the response.",
     1490                            "type": "string"
     1491                        },
    13731492                        "after": {
    13741493                            "required": false,
    13751494                            "description": "Limit response to posts published after a given ISO8601 compliant date.",