WP_REST_Controller

Core base controller for managing and interacting with REST API items.

Defined (1)

The class is defined in the following location(s).

/includes/vendor/abstract-wp-rest-controller.php  
  1. abstract class WP_REST_Controller { 
  2.  
  3. /** 
  4. * The namespace of this controller's route. 
  5. * @var string 
  6. */ 
  7. protected $namespace; 
  8.  
  9. /** 
  10. * The base of this controller's route. 
  11. * @var string 
  12. */ 
  13. protected $rest_base; 
  14.  
  15. /** 
  16. * Register the routes for the objects of the controller. 
  17. */ 
  18. public function register_routes() { 
  19. wc_doing_it_wrong( 'WP_REST_Controller::register_routes', __( 'The register_routes() method must be overriden', 'woocommerce' ), 'WPAPI-2.0' ); 
  20.  
  21. /** 
  22. * Check if a given request has access to get items. 
  23. * @param WP_REST_Request $request Full data about the request. 
  24. * @return WP_Error|boolean 
  25. */ 
  26. public function get_items_permissions_check( $request ) { 
  27. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  28.  
  29. /** 
  30. * Get a collection of items. 
  31. * @param WP_REST_Request $request Full data about the request. 
  32. * @return WP_Error|WP_REST_Response 
  33. */ 
  34. public function get_items( $request ) { 
  35. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  36.  
  37. /** 
  38. * Check if a given request has access to get a specific item. 
  39. * @param WP_REST_Request $request Full data about the request. 
  40. * @return WP_Error|boolean 
  41. */ 
  42. public function get_item_permissions_check( $request ) { 
  43. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  44.  
  45. /** 
  46. * Get one item from the collection. 
  47. * @param WP_REST_Request $request Full data about the request. 
  48. * @return WP_Error|WP_REST_Response 
  49. */ 
  50. public function get_item( $request ) { 
  51. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  52.  
  53. /** 
  54. * Check if a given request has access to create items. 
  55. * @param WP_REST_Request $request Full data about the request. 
  56. * @return WP_Error|boolean 
  57. */ 
  58. public function create_item_permissions_check( $request ) { 
  59. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  60.  
  61. /** 
  62. * Create one item from the collection. 
  63. * @param WP_REST_Request $request Full data about the request. 
  64. * @return WP_Error|WP_REST_Response 
  65. */ 
  66. public function create_item( $request ) { 
  67. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  68.  
  69. /** 
  70. * Check if a given request has access to update a specific item. 
  71. * @param WP_REST_Request $request Full data about the request. 
  72. * @return WP_Error|boolean 
  73. */ 
  74. public function update_item_permissions_check( $request ) { 
  75. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  76.  
  77. /** 
  78. * Update one item from the collection. 
  79. * @param WP_REST_Request $request Full data about the request. 
  80. * @return WP_Error|WP_REST_Response 
  81. */ 
  82. public function update_item( $request ) { 
  83. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  84.  
  85. /** 
  86. * Check if a given request has access to delete a specific item. 
  87. * @param WP_REST_Request $request Full data about the request. 
  88. * @return WP_Error|boolean 
  89. */ 
  90. public function delete_item_permissions_check( $request ) { 
  91. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  92.  
  93. /** 
  94. * Delete one item from the collection. 
  95. * @param WP_REST_Request $request Full data about the request. 
  96. * @return WP_Error|WP_REST_Response 
  97. */ 
  98. public function delete_item( $request ) { 
  99. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  100.  
  101. /** 
  102. * Prepare the item for create or update operation. 
  103. * @param WP_REST_Request $request Request object. 
  104. * @return WP_Error|object $prepared_item 
  105. */ 
  106. protected function prepare_item_for_database( $request ) { 
  107. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  108.  
  109. /** 
  110. * Prepare the item for the REST response. 
  111. * @param mixed $item WordPress representation of the item. 
  112. * @param WP_REST_Request $request Request object. 
  113. * @return WP_REST_Response $response 
  114. */ 
  115. public function prepare_item_for_response( $item, $request ) { 
  116. return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass.", 'woocommerce' ), __METHOD__ ), array( 'status' => 405 ) ); 
  117.  
  118. /** 
  119. * Prepare a response for inserting into a collection. 
  120. * @param WP_REST_Response $response Response object. 
  121. * @return array Response data, ready for insertion into collection data. 
  122. */ 
  123. public function prepare_response_for_collection( $response ) { 
  124. if ( ! ( $response instanceof WP_REST_Response ) ) { 
  125. return $response; 
  126.  
  127. $data = (array) $response->get_data(); 
  128. $server = rest_get_server(); 
  129.  
  130. if ( method_exists( $server, 'get_compact_response_links' ) ) { 
  131. $links = call_user_func( array( $server, 'get_compact_response_links' ), $response ); 
  132. } else { 
  133. $links = call_user_func( array( $server, 'get_response_links' ), $response ); 
  134.  
  135. if ( ! empty( $links ) ) { 
  136. $data['_links'] = $links; 
  137.  
  138. return $data; 
  139.  
  140. /** 
  141. * Filter a response based on the context defined in the schema. 
  142. * @param array $data 
  143. * @param string $context 
  144. * @return array 
  145. */ 
  146. public function filter_response_by_context( $data, $context ) { 
  147.  
  148. $schema = $this->get_item_schema(); 
  149. foreach ( $data as $key => $value ) { 
  150. if ( empty( $schema['properties'][ $key ] ) || empty( $schema['properties'][ $key ]['context'] ) ) { 
  151. continue; 
  152.  
  153. if ( ! in_array( $context, $schema['properties'][ $key ]['context'] ) ) { 
  154. unset( $data[ $key ] ); 
  155.  
  156. if ( 'object' === $schema['properties'][ $key ]['type'] && ! empty( $schema['properties'][ $key ]['properties'] ) ) { 
  157. foreach ( $schema['properties'][ $key ]['properties'] as $attribute => $details ) { 
  158. if ( empty( $details['context'] ) ) { 
  159. continue; 
  160. if ( ! in_array( $context, $details['context'] ) ) { 
  161. if ( isset( $data[ $key ][ $attribute ] ) ) { 
  162. unset( $data[ $key ][ $attribute ] ); 
  163.  
  164. return $data; 
  165.  
  166. /** 
  167. * Get the item's schema, conforming to JSON Schema. 
  168. * @return array 
  169. */ 
  170. public function get_item_schema() { 
  171. return $this->add_additional_fields_schema( array() ); 
  172.  
  173. /** 
  174. * Get the item's schema for display / public consumption purposes. 
  175. * @return array 
  176. */ 
  177. public function get_public_item_schema() { 
  178.  
  179. $schema = $this->get_item_schema(); 
  180.  
  181. foreach ( $schema['properties'] as &$property ) { 
  182. if ( isset( $property['arg_options'] ) ) { 
  183. unset( $property['arg_options'] ); 
  184.  
  185. return $schema; 
  186.  
  187. /** 
  188. * Get the query params for collections. 
  189. * @return array 
  190. */ 
  191. public function get_collection_params() { 
  192. return array( 
  193. 'context' => $this->get_context_param(),  
  194. 'page' => array( 
  195. 'description' => __( 'Current page of the collection.', 'woocommerce' ),  
  196. 'type' => 'integer',  
  197. 'default' => 1,  
  198. 'sanitize_callback' => 'absint',  
  199. 'validate_callback' => 'rest_validate_request_arg',  
  200. 'minimum' => 1,  
  201. ),  
  202. 'per_page' => array( 
  203. 'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ),  
  204. 'type' => 'integer',  
  205. 'default' => 10,  
  206. 'minimum' => 1,  
  207. 'maximum' => 100,  
  208. 'sanitize_callback' => 'absint',  
  209. 'validate_callback' => 'rest_validate_request_arg',  
  210. ),  
  211. 'search' => array( 
  212. 'description' => __( 'Limit results to those matching a string.', 'woocommerce' ),  
  213. 'type' => 'string',  
  214. 'sanitize_callback' => 'sanitize_text_field',  
  215. 'validate_callback' => 'rest_validate_request_arg',  
  216. ),  
  217. ); 
  218.  
  219. /** 
  220. * Get the magical context param. 
  221. * Ensures consistent description between endpoints, and populates enum from schema. 
  222. * @param array $args 
  223. * @return array 
  224. */ 
  225. public function get_context_param( $args = array() ) { 
  226. $param_details = array( 
  227. 'description' => __( 'Scope under which the request is made; determines fields present in response.', 'woocommerce' ),  
  228. 'type' => 'string',  
  229. 'sanitize_callback' => 'sanitize_key',  
  230. 'validate_callback' => 'rest_validate_request_arg',  
  231. ); 
  232. $schema = $this->get_item_schema(); 
  233. if ( empty( $schema['properties'] ) ) { 
  234. return array_merge( $param_details, $args ); 
  235. $contexts = array(); 
  236. foreach ( $schema['properties'] as $key => $attributes ) { 
  237. if ( ! empty( $attributes['context'] ) ) { 
  238. $contexts = array_merge( $contexts, $attributes['context'] ); 
  239. if ( ! empty( $contexts ) ) { 
  240. $param_details['enum'] = array_unique( $contexts ); 
  241. rsort( $param_details['enum'] ); 
  242. return array_merge( $param_details, $args ); 
  243.  
  244. /** 
  245. * Add the values from additional fields to a data object. 
  246. * @param array $object 
  247. * @param WP_REST_Request $request 
  248. * @return array modified object with additional fields. 
  249. */ 
  250. protected function add_additional_fields_to_object( $object, $request ) { 
  251.  
  252. $additional_fields = $this->get_additional_fields(); 
  253.  
  254. foreach ( $additional_fields as $field_name => $field_options ) { 
  255.  
  256. if ( ! $field_options['get_callback'] ) { 
  257. continue; 
  258.  
  259. $object[ $field_name ] = call_user_func( $field_options['get_callback'], $object, $field_name, $request, $this->get_object_type() ); 
  260.  
  261. return $object; 
  262.  
  263. /** 
  264. * Update the values of additional fields added to a data object. 
  265. * @param array $object 
  266. * @param WP_REST_Request $request 
  267. */ 
  268. protected function update_additional_fields_for_object( $object, $request ) { 
  269.  
  270. $additional_fields = $this->get_additional_fields(); 
  271.  
  272. foreach ( $additional_fields as $field_name => $field_options ) { 
  273.  
  274. if ( ! $field_options['update_callback'] ) { 
  275. continue; 
  276.  
  277. // Don't run the update callbacks if the data wasn't passed in the request. 
  278. if ( ! isset( $request[ $field_name ] ) ) { 
  279. continue; 
  280.  
  281. call_user_func( $field_options['update_callback'], $request[ $field_name ], $object, $field_name, $request, $this->get_object_type() ); 
  282.  
  283. /** 
  284. * Add the schema from additional fields to an schema array. 
  285. * The type of object is inferred from the passed schema. 
  286. * @param array $schema Schema array. 
  287. * @return array $schema Schema array. 
  288. */ 
  289. protected function add_additional_fields_schema( $schema ) { 
  290. if ( empty( $schema['title'] ) ) { 
  291. return $schema; 
  292.  
  293. /** 
  294. * Can't use $this->get_object_type otherwise we cause an inf loop. 
  295. */ 
  296. $object_type = $schema['title']; 
  297.  
  298. $additional_fields = $this->get_additional_fields( $object_type ); 
  299.  
  300. foreach ( $additional_fields as $field_name => $field_options ) { 
  301. if ( ! $field_options['schema'] ) { 
  302. continue; 
  303.  
  304. $schema['properties'][ $field_name ] = $field_options['schema']; 
  305.  
  306. return $schema; 
  307.  
  308. /** 
  309. * Get all the registered additional fields for a given object-type. 
  310. * @param string $object_type 
  311. * @return array 
  312. */ 
  313. protected function get_additional_fields( $object_type = null ) { 
  314.  
  315. if ( ! $object_type ) { 
  316. $object_type = $this->get_object_type(); 
  317.  
  318. if ( ! $object_type ) { 
  319. return array(); 
  320.  
  321. global $wp_rest_additional_fields; 
  322.  
  323. if ( ! $wp_rest_additional_fields || ! isset( $wp_rest_additional_fields[ $object_type ] ) ) { 
  324. return array(); 
  325.  
  326. return $wp_rest_additional_fields[ $object_type ]; 
  327.  
  328. /** 
  329. * Get the object type this controller is responsible for managing. 
  330. * @return string 
  331. */ 
  332. protected function get_object_type() { 
  333. $schema = $this->get_item_schema(); 
  334.  
  335. if ( ! $schema || ! isset( $schema['title'] ) ) { 
  336. return null; 
  337.  
  338. return $schema['title']; 
  339.  
  340. /** 
  341. * Get an array of endpoint arguments from the item schema for the controller. 
  342. * @param string $method HTTP method of the request. The arguments 
  343. * for `CREATABLE` requests are checked for required 
  344. * values and may fall-back to a given default, this 
  345. * is not done on `EDITABLE` requests. Default is 
  346. * WP_REST_Server::CREATABLE. 
  347. * @return array $endpoint_args 
  348. */ 
  349. public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE ) { 
  350.  
  351. $schema = $this->get_item_schema(); 
  352. $schema_properties = ! empty( $schema['properties'] ) ? $schema['properties'] : array(); 
  353. $endpoint_args = array(); 
  354.  
  355. foreach ( $schema_properties as $field_id => $params ) { 
  356.  
  357. // Arguments specified as `readonly` are not allowed to be set. 
  358. if ( ! empty( $params['readonly'] ) ) { 
  359. continue; 
  360.  
  361. $endpoint_args[ $field_id ] = array( 
  362. 'validate_callback' => 'rest_validate_request_arg',  
  363. 'sanitize_callback' => 'rest_sanitize_request_arg',  
  364. ); 
  365.  
  366. if ( isset( $params['description'] ) ) { 
  367. $endpoint_args[ $field_id ]['description'] = $params['description']; 
  368.  
  369. if ( WP_REST_Server::CREATABLE === $method && isset( $params['default'] ) ) { 
  370. $endpoint_args[ $field_id ]['default'] = $params['default']; 
  371.  
  372. if ( WP_REST_Server::CREATABLE === $method && ! empty( $params['required'] ) ) { 
  373. $endpoint_args[ $field_id ]['required'] = true; 
  374.  
  375. foreach ( array( 'type', 'format', 'enum' ) as $schema_prop ) { 
  376. if ( isset( $params[ $schema_prop ] ) ) { 
  377. $endpoint_args[ $field_id ][ $schema_prop ] = $params[ $schema_prop ]; 
  378.  
  379. // Merge in any options provided by the schema property. 
  380. if ( isset( $params['arg_options'] ) ) { 
  381.  
  382. // Only use required / default from arg_options on CREATABLE endpoints. 
  383. if ( WP_REST_Server::CREATABLE !== $method ) { 
  384. $params['arg_options'] = array_diff_key( $params['arg_options'], array( 'required' => '', 'default' => '' ) ); 
  385.  
  386. $endpoint_args[ $field_id ] = array_merge( $endpoint_args[ $field_id ], $params['arg_options'] ); 
  387.  
  388. return $endpoint_args;