wp/wp-includes/rest-api/endpoints/class-wp-rest-controller.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    29 	 * @var string
    29 	 * @var string
    30 	 */
    30 	 */
    31 	protected $rest_base;
    31 	protected $rest_base;
    32 
    32 
    33 	/**
    33 	/**
       
    34 	 * Cached results of get_item_schema.
       
    35 	 *
       
    36 	 * @since 5.3.0
       
    37 	 * @var array
       
    38 	 */
       
    39 	protected $schema;
       
    40 
       
    41 	/**
    34 	 * Registers the routes for the objects of the controller.
    42 	 * Registers the routes for the objects of the controller.
    35 	 *
    43 	 *
    36 	 * @since 4.7.0
    44 	 * @since 4.7.0
       
    45 	 *
       
    46 	 * @see register_rest_route()
    37 	 */
    47 	 */
    38 	public function register_routes() {
    48 	public function register_routes() {
    39 		/* translators: %s: register_routes() */
    49 		_doing_it_wrong(
    40 		_doing_it_wrong( 'WP_REST_Controller::register_routes', sprintf( __( "Method '%s' must be overridden." ), __METHOD__ ), '4.7' );
    50 			'WP_REST_Controller::register_routes',
       
    51 			/* translators: %s: register_routes() */
       
    52 			sprintf( __( "Method '%s' must be overridden." ), __METHOD__ ),
       
    53 			'4.7'
       
    54 		);
    41 	}
    55 	}
    42 
    56 
    43 	/**
    57 	/**
    44 	 * Checks if a given request has access to get items.
    58 	 * Checks if a given request has access to get items.
    45 	 *
    59 	 *
    46 	 * @since 4.7.0
    60 	 * @since 4.7.0
    47 	 *
    61 	 *
    48 	 * @param WP_REST_Request $request Full data about the request.
    62 	 * @param WP_REST_Request $request Full details about the request.
    49 	 * @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
    63 	 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
    50 	 */
    64 	 */
    51 	public function get_items_permissions_check( $request ) {
    65 	public function get_items_permissions_check( $request ) {
    52 		/* translators: %s: method name */
    66 		return new WP_Error(
    53 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
    67 			'invalid-method',
       
    68 			/* translators: %s: Method name. */
       
    69 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
    70 			array( 'status' => 405 )
       
    71 		);
    54 	}
    72 	}
    55 
    73 
    56 	/**
    74 	/**
    57 	 * Retrieves a collection of items.
    75 	 * Retrieves a collection of items.
    58 	 *
    76 	 *
    59 	 * @since 4.7.0
    77 	 * @since 4.7.0
    60 	 *
    78 	 *
    61 	 * @param WP_REST_Request $request Full data about the request.
    79 	 * @param WP_REST_Request $request Full details about the request.
    62 	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
    80 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
    63 	 */
    81 	 */
    64 	public function get_items( $request ) {
    82 	public function get_items( $request ) {
    65 		/* translators: %s: method name */
    83 		return new WP_Error(
    66 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
    84 			'invalid-method',
       
    85 			/* translators: %s: Method name. */
       
    86 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
    87 			array( 'status' => 405 )
       
    88 		);
    67 	}
    89 	}
    68 
    90 
    69 	/**
    91 	/**
    70 	 * Checks if a given request has access to get a specific item.
    92 	 * Checks if a given request has access to get a specific item.
    71 	 *
    93 	 *
    72 	 * @since 4.7.0
    94 	 * @since 4.7.0
    73 	 *
    95 	 *
    74 	 * @param WP_REST_Request $request Full data about the request.
    96 	 * @param WP_REST_Request $request Full details about the request.
    75 	 * @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise.
    97 	 * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
    76 	 */
    98 	 */
    77 	public function get_item_permissions_check( $request ) {
    99 	public function get_item_permissions_check( $request ) {
    78 		/* translators: %s: method name */
   100 		return new WP_Error(
    79 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   101 			'invalid-method',
       
   102 			/* translators: %s: Method name. */
       
   103 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   104 			array( 'status' => 405 )
       
   105 		);
    80 	}
   106 	}
    81 
   107 
    82 	/**
   108 	/**
    83 	 * Retrieves one item from the collection.
   109 	 * Retrieves one item from the collection.
    84 	 *
   110 	 *
    85 	 * @since 4.7.0
   111 	 * @since 4.7.0
    86 	 *
   112 	 *
    87 	 * @param WP_REST_Request $request Full data about the request.
   113 	 * @param WP_REST_Request $request Full details about the request.
    88 	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
   114 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
    89 	 */
   115 	 */
    90 	public function get_item( $request ) {
   116 	public function get_item( $request ) {
    91 		/* translators: %s: method name */
   117 		return new WP_Error(
    92 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   118 			'invalid-method',
       
   119 			/* translators: %s: Method name. */
       
   120 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   121 			array( 'status' => 405 )
       
   122 		);
    93 	}
   123 	}
    94 
   124 
    95 	/**
   125 	/**
    96 	 * Checks if a given request has access to create items.
   126 	 * Checks if a given request has access to create items.
    97 	 *
   127 	 *
    98 	 * @since 4.7.0
   128 	 * @since 4.7.0
    99 	 *
   129 	 *
   100 	 * @param WP_REST_Request $request Full data about the request.
   130 	 * @param WP_REST_Request $request Full details about the request.
   101 	 * @return WP_Error|bool True if the request has access to create items, WP_Error object otherwise.
   131 	 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
   102 	 */
   132 	 */
   103 	public function create_item_permissions_check( $request ) {
   133 	public function create_item_permissions_check( $request ) {
   104 		/* translators: %s: method name */
   134 		return new WP_Error(
   105 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   135 			'invalid-method',
       
   136 			/* translators: %s: Method name. */
       
   137 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   138 			array( 'status' => 405 )
       
   139 		);
   106 	}
   140 	}
   107 
   141 
   108 	/**
   142 	/**
   109 	 * Creates one item from the collection.
   143 	 * Creates one item from the collection.
   110 	 *
   144 	 *
   111 	 * @since 4.7.0
   145 	 * @since 4.7.0
   112 	 *
   146 	 *
   113 	 * @param WP_REST_Request $request Full data about the request.
   147 	 * @param WP_REST_Request $request Full details about the request.
   114 	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
   148 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
   115 	 */
   149 	 */
   116 	public function create_item( $request ) {
   150 	public function create_item( $request ) {
   117 		/* translators: %s: method name */
   151 		return new WP_Error(
   118 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   152 			'invalid-method',
       
   153 			/* translators: %s: Method name. */
       
   154 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   155 			array( 'status' => 405 )
       
   156 		);
   119 	}
   157 	}
   120 
   158 
   121 	/**
   159 	/**
   122 	 * Checks if a given request has access to update a specific item.
   160 	 * Checks if a given request has access to update a specific item.
   123 	 *
   161 	 *
   124 	 * @since 4.7.0
   162 	 * @since 4.7.0
   125 	 *
   163 	 *
   126 	 * @param WP_REST_Request $request Full data about the request.
   164 	 * @param WP_REST_Request $request Full details about the request.
   127 	 * @return WP_Error|bool True if the request has access to update the item, WP_Error object otherwise.
   165 	 * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
   128 	 */
   166 	 */
   129 	public function update_item_permissions_check( $request ) {
   167 	public function update_item_permissions_check( $request ) {
   130 		/* translators: %s: method name */
   168 		return new WP_Error(
   131 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   169 			'invalid-method',
       
   170 			/* translators: %s: Method name. */
       
   171 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   172 			array( 'status' => 405 )
       
   173 		);
   132 	}
   174 	}
   133 
   175 
   134 	/**
   176 	/**
   135 	 * Updates one item from the collection.
   177 	 * Updates one item from the collection.
   136 	 *
   178 	 *
   137 	 * @since 4.7.0
   179 	 * @since 4.7.0
   138 	 *
   180 	 *
   139 	 * @param WP_REST_Request $request Full data about the request.
   181 	 * @param WP_REST_Request $request Full details about the request.
   140 	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
   182 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
   141 	 */
   183 	 */
   142 	public function update_item( $request ) {
   184 	public function update_item( $request ) {
   143 		/* translators: %s: method name */
   185 		return new WP_Error(
   144 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   186 			'invalid-method',
       
   187 			/* translators: %s: Method name. */
       
   188 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   189 			array( 'status' => 405 )
       
   190 		);
   145 	}
   191 	}
   146 
   192 
   147 	/**
   193 	/**
   148 	 * Checks if a given request has access to delete a specific item.
   194 	 * Checks if a given request has access to delete a specific item.
   149 	 *
   195 	 *
   150 	 * @since 4.7.0
   196 	 * @since 4.7.0
   151 	 *
   197 	 *
   152 	 * @param WP_REST_Request $request Full data about the request.
   198 	 * @param WP_REST_Request $request Full details about the request.
   153 	 * @return WP_Error|bool True if the request has access to delete the item, WP_Error object otherwise.
   199 	 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
   154 	 */
   200 	 */
   155 	public function delete_item_permissions_check( $request ) {
   201 	public function delete_item_permissions_check( $request ) {
   156 		/* translators: %s: method name */
   202 		return new WP_Error(
   157 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   203 			'invalid-method',
       
   204 			/* translators: %s: Method name. */
       
   205 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   206 			array( 'status' => 405 )
       
   207 		);
   158 	}
   208 	}
   159 
   209 
   160 	/**
   210 	/**
   161 	 * Deletes one item from the collection.
   211 	 * Deletes one item from the collection.
   162 	 *
   212 	 *
   163 	 * @since 4.7.0
   213 	 * @since 4.7.0
   164 	 *
   214 	 *
   165 	 * @param WP_REST_Request $request Full data about the request.
   215 	 * @param WP_REST_Request $request Full details about the request.
   166 	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
   216 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
   167 	 */
   217 	 */
   168 	public function delete_item( $request ) {
   218 	public function delete_item( $request ) {
   169 		/* translators: %s: method name */
   219 		return new WP_Error(
   170 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   220 			'invalid-method',
       
   221 			/* translators: %s: Method name. */
       
   222 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   223 			array( 'status' => 405 )
       
   224 		);
   171 	}
   225 	}
   172 
   226 
   173 	/**
   227 	/**
   174 	 * Prepares one item for create or update operation.
   228 	 * Prepares one item for create or update operation.
   175 	 *
   229 	 *
   176 	 * @since 4.7.0
   230 	 * @since 4.7.0
   177 	 *
   231 	 *
   178 	 * @param WP_REST_Request $request Request object.
   232 	 * @param WP_REST_Request $request Request object.
   179 	 * @return WP_Error|object The prepared item, or WP_Error object on failure.
   233 	 * @return object|WP_Error The prepared item, or WP_Error object on failure.
   180 	 */
   234 	 */
   181 	protected function prepare_item_for_database( $request ) {
   235 	protected function prepare_item_for_database( $request ) {
   182 		/* translators: %s: method name */
   236 		return new WP_Error(
   183 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   237 			'invalid-method',
       
   238 			/* translators: %s: Method name. */
       
   239 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   240 			array( 'status' => 405 )
       
   241 		);
   184 	}
   242 	}
   185 
   243 
   186 	/**
   244 	/**
   187 	 * Prepares the item for the REST response.
   245 	 * Prepares the item for the REST response.
   188 	 *
   246 	 *
   189 	 * @since 4.7.0
   247 	 * @since 4.7.0
   190 	 *
   248 	 *
   191 	 * @param mixed           $item    WordPress representation of the item.
   249 	 * @param mixed           $item    WordPress representation of the item.
   192 	 * @param WP_REST_Request $request Request object.
   250 	 * @param WP_REST_Request $request Request object.
   193 	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
   251 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
   194 	 */
   252 	 */
   195 	public function prepare_item_for_response( $item, $request ) {
   253 	public function prepare_item_for_response( $item, $request ) {
   196 		/* translators: %s: method name */
   254 		return new WP_Error(
   197 		return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) );
   255 			'invalid-method',
       
   256 			/* translators: %s: Method name. */
       
   257 			sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ),
       
   258 			array( 'status' => 405 )
       
   259 		);
   198 	}
   260 	}
   199 
   261 
   200 	/**
   262 	/**
   201 	 * Prepares a response for insertion into a collection.
   263 	 * Prepares a response for insertion into a collection.
   202 	 *
   264 	 *
   210 			return $response;
   272 			return $response;
   211 		}
   273 		}
   212 
   274 
   213 		$data   = (array) $response->get_data();
   275 		$data   = (array) $response->get_data();
   214 		$server = rest_get_server();
   276 		$server = rest_get_server();
   215 		$links  = $server->get_compact_response_links( $response );
   277 		$links  = $server::get_compact_response_links( $response );
   216 
   278 
   217 		if ( ! empty( $links ) ) {
   279 		if ( ! empty( $links ) ) {
   218 			$data['_links'] = $links;
   280 			$data['_links'] = $links;
   219 		}
   281 		}
   220 
   282 
   224 	/**
   286 	/**
   225 	 * Filters a response based on the context defined in the schema.
   287 	 * Filters a response based on the context defined in the schema.
   226 	 *
   288 	 *
   227 	 * @since 4.7.0
   289 	 * @since 4.7.0
   228 	 *
   290 	 *
   229 	 * @param array  $data    Response data to fiter.
   291 	 * @param array  $data    Response data to filter.
   230 	 * @param string $context Context defined in the schema.
   292 	 * @param string $context Context defined in the schema.
   231 	 * @return array Filtered response.
   293 	 * @return array Filtered response.
   232 	 */
   294 	 */
   233 	public function filter_response_by_context( $data, $context ) {
   295 	public function filter_response_by_context( $data, $context ) {
   234 
   296 
   235 		$schema = $this->get_item_schema();
   297 		$schema = $this->get_item_schema();
   236 
   298 
   237 		foreach ( $data as $key => $value ) {
   299 		return rest_filter_response_by_context( $data, $schema, $context );
   238 			if ( empty( $schema['properties'][ $key ] ) || empty( $schema['properties'][ $key ]['context'] ) ) {
       
   239 				continue;
       
   240 			}
       
   241 
       
   242 			if ( ! in_array( $context, $schema['properties'][ $key ]['context'], true ) ) {
       
   243 				unset( $data[ $key ] );
       
   244 				continue;
       
   245 			}
       
   246 
       
   247 			if ( 'object' === $schema['properties'][ $key ]['type'] && ! empty( $schema['properties'][ $key ]['properties'] ) ) {
       
   248 				foreach ( $schema['properties'][ $key ]['properties'] as $attribute => $details ) {
       
   249 					if ( empty( $details['context'] ) ) {
       
   250 						continue;
       
   251 					}
       
   252 
       
   253 					if ( ! in_array( $context, $details['context'], true ) ) {
       
   254 						if ( isset( $data[ $key ][ $attribute ] ) ) {
       
   255 							unset( $data[ $key ][ $attribute ] );
       
   256 						}
       
   257 					}
       
   258 				}
       
   259 			}
       
   260 		}
       
   261 
       
   262 		return $data;
       
   263 	}
   300 	}
   264 
   301 
   265 	/**
   302 	/**
   266 	 * Retrieves the item's schema, conforming to JSON Schema.
   303 	 * Retrieves the item's schema, conforming to JSON Schema.
   267 	 *
   304 	 *
   282 	 */
   319 	 */
   283 	public function get_public_item_schema() {
   320 	public function get_public_item_schema() {
   284 
   321 
   285 		$schema = $this->get_item_schema();
   322 		$schema = $this->get_item_schema();
   286 
   323 
   287 		foreach ( $schema['properties'] as &$property ) {
   324 		if ( ! empty( $schema['properties'] ) ) {
   288 			unset( $property['arg_options'] );
   325 			foreach ( $schema['properties'] as &$property ) {
       
   326 				unset( $property['arg_options'] );
       
   327 			}
   289 		}
   328 		}
   290 
   329 
   291 		return $schema;
   330 		return $schema;
   292 	}
   331 	}
   293 
   332 
   370 	/**
   409 	/**
   371 	 * Adds the values from additional fields to a data object.
   410 	 * Adds the values from additional fields to a data object.
   372 	 *
   411 	 *
   373 	 * @since 4.7.0
   412 	 * @since 4.7.0
   374 	 *
   413 	 *
   375 	 * @param array           $object  Data object.
   414 	 * @param array           $prepared Prepared response array.
   376 	 * @param WP_REST_Request $request Full details about the request.
   415 	 * @param WP_REST_Request $request  Full details about the request.
   377 	 * @return array Modified data object with additional fields.
   416 	 * @return array Modified data object with additional fields.
   378 	 */
   417 	 */
   379 	protected function add_additional_fields_to_object( $object, $request ) {
   418 	protected function add_additional_fields_to_object( $prepared, $request ) {
   380 
   419 
   381 		$additional_fields = $this->get_additional_fields();
   420 		$additional_fields = $this->get_additional_fields();
   382 
   421 
   383 		$requested_fields = $this->get_fields_for_response( $request );
   422 		$requested_fields = $this->get_fields_for_response( $request );
   384 
   423 
   385 		foreach ( $additional_fields as $field_name => $field_options ) {
   424 		foreach ( $additional_fields as $field_name => $field_options ) {
   386 
       
   387 			if ( ! $field_options['get_callback'] ) {
   425 			if ( ! $field_options['get_callback'] ) {
   388 				continue;
   426 				continue;
   389 			}
   427 			}
   390 
   428 
   391 			if ( ! in_array( $field_name, $requested_fields, true ) ) {
   429 			if ( ! rest_is_field_included( $field_name, $requested_fields ) ) {
   392 				continue;
   430 				continue;
   393 			}
   431 			}
   394 
   432 
   395 			$object[ $field_name ] = call_user_func( $field_options['get_callback'], $object, $field_name, $request, $this->get_object_type() );
   433 			$prepared[ $field_name ] = call_user_func( $field_options['get_callback'], $prepared, $field_name, $request, $this->get_object_type() );
   396 		}
   434 		}
   397 
   435 
   398 		return $object;
   436 		return $prepared;
   399 	}
   437 	}
   400 
   438 
   401 	/**
   439 	/**
   402 	 * Updates the values of additional fields added to a data object.
   440 	 * Updates the values of additional fields added to a data object.
   403 	 *
   441 	 *
   404 	 * @since 4.7.0
   442 	 * @since 4.7.0
   405 	 *
   443 	 *
   406 	 * @param array           $object  Data Object.
   444 	 * @param object          $object  Data model like WP_Term or WP_Post.
   407 	 * @param WP_REST_Request $request Full details about the request.
   445 	 * @param WP_REST_Request $request Full details about the request.
   408 	 * @return bool|WP_Error True on success, WP_Error object if a field cannot be updated.
   446 	 * @return bool|WP_Error True on success, WP_Error object if a field cannot be updated.
   409 	 */
   447 	 */
   410 	protected function update_additional_fields_for_object( $object, $request ) {
   448 	protected function update_additional_fields_for_object( $object, $request ) {
   411 		$additional_fields = $this->get_additional_fields();
   449 		$additional_fields = $this->get_additional_fields();
   464 	/**
   502 	/**
   465 	 * Retrieves all of the registered additional fields for a given object-type.
   503 	 * Retrieves all of the registered additional fields for a given object-type.
   466 	 *
   504 	 *
   467 	 * @since 4.7.0
   505 	 * @since 4.7.0
   468 	 *
   506 	 *
   469 	 * @param  string $object_type Optional. The object type.
   507 	 * @param string $object_type Optional. The object type.
   470 	 * @return array Registered additional fields (if any), empty array if none or if the object type could
   508 	 * @return array Registered additional fields (if any), empty array if none or if the object type could
   471 	 *               not be inferred.
   509 	 *               not be inferred.
   472 	 */
   510 	 */
   473 	protected function get_additional_fields( $object_type = null ) {
   511 	protected function get_additional_fields( $object_type = null ) {
   474 
   512 
   515 	 *
   553 	 *
   516 	 * @param WP_REST_Request $request Full details about the request.
   554 	 * @param WP_REST_Request $request Full details about the request.
   517 	 * @return array Fields to be included in the response.
   555 	 * @return array Fields to be included in the response.
   518 	 */
   556 	 */
   519 	public function get_fields_for_response( $request ) {
   557 	public function get_fields_for_response( $request ) {
   520 		$schema = $this->get_item_schema();
   558 		$schema     = $this->get_item_schema();
   521 		$fields = isset( $schema['properties'] ) ? array_keys( $schema['properties'] ) : array();
   559 		$properties = isset( $schema['properties'] ) ? $schema['properties'] : array();
   522 
   560 
   523 		$additional_fields = $this->get_additional_fields();
   561 		$additional_fields = $this->get_additional_fields();
       
   562 
   524 		foreach ( $additional_fields as $field_name => $field_options ) {
   563 		foreach ( $additional_fields as $field_name => $field_options ) {
   525 			// For back-compat, include any field with an empty schema
   564 			// For back-compat, include any field with an empty schema
   526 			// because it won't be present in $this->get_item_schema().
   565 			// because it won't be present in $this->get_item_schema().
   527 			if ( is_null( $field_options['schema'] ) ) {
   566 			if ( is_null( $field_options['schema'] ) ) {
   528 				$fields[] = $field_name;
   567 				$properties[ $field_name ] = $field_options;
   529 			}
   568 			}
   530 		}
   569 		}
       
   570 
       
   571 		// Exclude fields that specify a different context than the request context.
       
   572 		$context = $request['context'];
       
   573 		if ( $context ) {
       
   574 			foreach ( $properties as $name => $options ) {
       
   575 				if ( ! empty( $options['context'] ) && ! in_array( $context, $options['context'], true ) ) {
       
   576 					unset( $properties[ $name ] );
       
   577 				}
       
   578 			}
       
   579 		}
       
   580 
       
   581 		$fields = array_keys( $properties );
   531 
   582 
   532 		if ( ! isset( $request['_fields'] ) ) {
   583 		if ( ! isset( $request['_fields'] ) ) {
   533 			return $fields;
   584 			return $fields;
   534 		}
   585 		}
   535 		$requested_fields = wp_parse_list( $request['_fields'] );
   586 		$requested_fields = wp_parse_list( $request['_fields'] );
   540 		$requested_fields = array_map( 'trim', $requested_fields );
   591 		$requested_fields = array_map( 'trim', $requested_fields );
   541 		// Always persist 'id', because it can be needed for add_additional_fields_to_object().
   592 		// Always persist 'id', because it can be needed for add_additional_fields_to_object().
   542 		if ( in_array( 'id', $fields, true ) ) {
   593 		if ( in_array( 'id', $fields, true ) ) {
   543 			$requested_fields[] = 'id';
   594 			$requested_fields[] = 'id';
   544 		}
   595 		}
   545 		return array_intersect( $fields, $requested_fields );
   596 		// Return the list of all requested fields which appear in the schema.
       
   597 		return array_reduce(
       
   598 			$requested_fields,
       
   599 			function( $response_fields, $field ) use ( $fields ) {
       
   600 				if ( in_array( $field, $fields, true ) ) {
       
   601 					$response_fields[] = $field;
       
   602 					return $response_fields;
       
   603 				}
       
   604 				// Check for nested fields if $field is not a direct match.
       
   605 				$nested_fields = explode( '.', $field );
       
   606 				// A nested field is included so long as its top-level property
       
   607 				// is present in the schema.
       
   608 				if ( in_array( $nested_fields[0], $fields, true ) ) {
       
   609 					$response_fields[] = $field;
       
   610 				}
       
   611 				return $response_fields;
       
   612 			},
       
   613 			array()
       
   614 		);
   546 	}
   615 	}
   547 
   616 
   548 	/**
   617 	/**
   549 	 * Retrieves an array of endpoint arguments from the item schema for the controller.
   618 	 * Retrieves an array of endpoint arguments from the item schema for the controller.
   550 	 *
   619 	 *
   555 	 *                       on `EDITABLE` requests. Default WP_REST_Server::CREATABLE.
   624 	 *                       on `EDITABLE` requests. Default WP_REST_Server::CREATABLE.
   556 	 * @return array Endpoint arguments.
   625 	 * @return array Endpoint arguments.
   557 	 */
   626 	 */
   558 	public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE ) {
   627 	public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE ) {
   559 
   628 
   560 		$schema            = $this->get_item_schema();
   629 		$schema                  = $this->get_item_schema();
   561 		$schema_properties = ! empty( $schema['properties'] ) ? $schema['properties'] : array();
   630 		$schema_properties       = ! empty( $schema['properties'] ) ? $schema['properties'] : array();
   562 		$endpoint_args     = array();
   631 		$endpoint_args           = array();
       
   632 		$valid_schema_properties = array(
       
   633 			'type',
       
   634 			'format',
       
   635 			'enum',
       
   636 			'items',
       
   637 			'properties',
       
   638 			'additionalProperties',
       
   639 			'minimum',
       
   640 			'maximum',
       
   641 			'exclusiveMinimum',
       
   642 			'exclusiveMaximum',
       
   643 			'minLength',
       
   644 			'maxLength',
       
   645 			'pattern',
       
   646 			'minItems',
       
   647 			'maxItems',
       
   648 			'uniqueItems',
       
   649 		);
   563 
   650 
   564 		foreach ( $schema_properties as $field_id => $params ) {
   651 		foreach ( $schema_properties as $field_id => $params ) {
   565 
   652 
   566 			// Arguments specified as `readonly` are not allowed to be set.
   653 			// Arguments specified as `readonly` are not allowed to be set.
   567 			if ( ! empty( $params['readonly'] ) ) {
   654 			if ( ! empty( $params['readonly'] ) ) {
   583 
   670 
   584 			if ( WP_REST_Server::CREATABLE === $method && ! empty( $params['required'] ) ) {
   671 			if ( WP_REST_Server::CREATABLE === $method && ! empty( $params['required'] ) ) {
   585 				$endpoint_args[ $field_id ]['required'] = true;
   672 				$endpoint_args[ $field_id ]['required'] = true;
   586 			}
   673 			}
   587 
   674 
   588 			foreach ( array( 'type', 'format', 'enum', 'items', 'properties', 'additionalProperties' ) as $schema_prop ) {
   675 			foreach ( $valid_schema_properties as $schema_prop ) {
   589 				if ( isset( $params[ $schema_prop ] ) ) {
   676 				if ( isset( $params[ $schema_prop ] ) ) {
   590 					$endpoint_args[ $field_id ][ $schema_prop ] = $params[ $schema_prop ];
   677 					$endpoint_args[ $field_id ][ $schema_prop ] = $params[ $schema_prop ];
   591 				}
   678 				}
   592 			}
   679 			}
   593 
   680