Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like WP_REST_Controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WP_REST_Controller, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
10 | abstract class WP_REST_Controller { |
||
11 | |||
12 | /** |
||
13 | * The namespace of this controller's route. |
||
14 | * |
||
15 | * @var string |
||
16 | */ |
||
17 | protected $namespace; |
||
18 | |||
19 | /** |
||
20 | * The base of this controller's route. |
||
21 | * |
||
22 | * @var string |
||
23 | */ |
||
24 | protected $rest_base; |
||
25 | |||
26 | /** |
||
27 | * Register the routes for the objects of the controller. |
||
28 | */ |
||
29 | public function register_routes() { |
||
32 | |||
33 | /** |
||
34 | * Check if a given request has access to get items. |
||
35 | * |
||
36 | * @param WP_REST_Request $request Full data about the request. |
||
37 | * @return WP_Error|boolean |
||
38 | */ |
||
39 | public function get_items_permissions_check( $request ) { |
||
42 | |||
43 | /** |
||
44 | * Get a collection of items. |
||
45 | * |
||
46 | * @param WP_REST_Request $request Full data about the request. |
||
47 | * @return WP_Error|WP_REST_Response |
||
48 | */ |
||
49 | public function get_items( $request ) { |
||
52 | |||
53 | /** |
||
54 | * Check if a given request has access to get a specific item. |
||
55 | * |
||
56 | * @param WP_REST_Request $request Full data about the request. |
||
57 | * @return WP_Error|boolean |
||
58 | */ |
||
59 | public function get_item_permissions_check( $request ) { |
||
62 | |||
63 | /** |
||
64 | * Get one item from the collection. |
||
65 | * |
||
66 | * @param WP_REST_Request $request Full data about the request. |
||
67 | * @return WP_Error|WP_REST_Response |
||
68 | */ |
||
69 | public function get_item( $request ) { |
||
72 | |||
73 | /** |
||
74 | * Check if a given request has access to create items. |
||
75 | * |
||
76 | * @param WP_REST_Request $request Full data about the request. |
||
77 | * @return WP_Error|boolean |
||
78 | */ |
||
79 | public function create_item_permissions_check( $request ) { |
||
82 | |||
83 | /** |
||
84 | * Create one item from the collection. |
||
85 | * |
||
86 | * @param WP_REST_Request $request Full data about the request. |
||
87 | * @return WP_Error|WP_REST_Response |
||
88 | */ |
||
89 | public function create_item( $request ) { |
||
92 | |||
93 | /** |
||
94 | * Check if a given request has access to update a specific item. |
||
95 | * |
||
96 | * @param WP_REST_Request $request Full data about the request. |
||
97 | * @return WP_Error|boolean |
||
98 | */ |
||
99 | public function update_item_permissions_check( $request ) { |
||
102 | |||
103 | /** |
||
104 | * Update one item from the collection. |
||
105 | * |
||
106 | * @param WP_REST_Request $request Full data about the request. |
||
107 | * @return WP_Error|WP_REST_Response |
||
108 | */ |
||
109 | public function update_item( $request ) { |
||
112 | |||
113 | /** |
||
114 | * Check if a given request has access to delete a specific item. |
||
115 | * |
||
116 | * @param WP_REST_Request $request Full data about the request. |
||
117 | * @return WP_Error|boolean |
||
118 | */ |
||
119 | public function delete_item_permissions_check( $request ) { |
||
122 | |||
123 | /** |
||
124 | * Delete one item from the collection. |
||
125 | * |
||
126 | * @param WP_REST_Request $request Full data about the request. |
||
127 | * @return WP_Error|WP_REST_Response |
||
128 | */ |
||
129 | public function delete_item( $request ) { |
||
132 | |||
133 | /** |
||
134 | * Prepare the item for create or update operation. |
||
135 | * |
||
136 | * @param WP_REST_Request $request Request object. |
||
137 | * @return WP_Error|object $prepared_item |
||
138 | */ |
||
139 | protected function prepare_item_for_database( $request ) { |
||
142 | |||
143 | /** |
||
144 | * Prepare the item for the REST response. |
||
145 | * |
||
146 | * @param mixed $item WordPress representation of the item. |
||
147 | * @param WP_REST_Request $request Request object. |
||
148 | * @return WP_REST_Response $response |
||
149 | */ |
||
150 | public function prepare_item_for_response( $item, $request ) { |
||
153 | |||
154 | /** |
||
155 | * Prepare a response for inserting into a collection. |
||
156 | * |
||
157 | * @param WP_REST_Response $response Response object. |
||
158 | * @return array Response data, ready for insertion into collection data. |
||
159 | */ |
||
160 | public function prepare_response_for_collection( $response ) { |
||
161 | if ( ! ( $response instanceof WP_REST_Response ) ) { |
||
162 | return $response; |
||
163 | } |
||
164 | |||
165 | $data = (array) $response->get_data(); |
||
166 | $server = rest_get_server(); |
||
167 | |||
168 | if ( method_exists( $server, 'get_compact_response_links' ) ) { |
||
169 | $links = call_user_func( array( $server, 'get_compact_response_links' ), $response ); |
||
170 | } else { |
||
171 | $links = call_user_func( array( $server, 'get_response_links' ), $response ); |
||
172 | } |
||
173 | |||
174 | if ( ! empty( $links ) ) { |
||
175 | $data['_links'] = $links; |
||
176 | } |
||
177 | |||
178 | return $data; |
||
179 | } |
||
180 | |||
181 | /** |
||
182 | * Filter a response based on the context defined in the schema. |
||
183 | * |
||
184 | * @param array $data |
||
185 | * @param string $context |
||
186 | * @return array |
||
187 | */ |
||
188 | public function filter_response_by_context( $data, $context ) { |
||
216 | |||
217 | /** |
||
218 | * Get the item's schema, conforming to JSON Schema. |
||
219 | * |
||
220 | * @return array |
||
221 | */ |
||
222 | public function get_item_schema() { |
||
225 | |||
226 | /** |
||
227 | * Get the item's schema for display / public consumption purposes. |
||
228 | * |
||
229 | * @return array |
||
230 | */ |
||
231 | public function get_public_item_schema() { |
||
243 | |||
244 | /** |
||
245 | * Get the query params for collections. |
||
246 | * |
||
247 | * @return array |
||
248 | */ |
||
249 | public function get_collection_params() { |
||
250 | return array( |
||
251 | 'context' => $this->get_context_param(), |
||
252 | 'page' => array( |
||
253 | 'description' => __( 'Current page of the collection.', 'woocommerce' ), |
||
254 | 'type' => 'integer', |
||
255 | 'default' => 1, |
||
256 | 'sanitize_callback' => 'absint', |
||
257 | 'validate_callback' => 'rest_validate_request_arg', |
||
258 | 'minimum' => 1, |
||
259 | ), |
||
260 | 'per_page' => array( |
||
261 | 'description' => __( 'Maximum number of items to be returned in result set.', 'woocommerce' ), |
||
262 | 'type' => 'integer', |
||
263 | 'default' => 10, |
||
264 | 'minimum' => 1, |
||
265 | 'maximum' => 100, |
||
266 | 'sanitize_callback' => 'absint', |
||
267 | 'validate_callback' => 'rest_validate_request_arg', |
||
268 | ), |
||
269 | 'search' => array( |
||
270 | 'description' => __( 'Limit results to those matching a string.', 'woocommerce' ), |
||
271 | 'type' => 'string', |
||
272 | 'sanitize_callback' => 'sanitize_text_field', |
||
273 | 'validate_callback' => 'rest_validate_request_arg', |
||
274 | ), |
||
275 | ); |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Get the magical context param. |
||
280 | * |
||
281 | * Ensures consistent description between endpoints, and populates enum from schema. |
||
282 | * |
||
283 | * @param array $args |
||
284 | * @return array |
||
285 | */ |
||
286 | public function get_context_param( $args = array() ) { |
||
287 | $param_details = array( |
||
288 | 'description' => __( 'Scope under which the request is made; determines fields present in response.', 'woocommerce' ), |
||
289 | 'type' => 'string', |
||
290 | 'sanitize_callback' => 'sanitize_key', |
||
291 | 'validate_callback' => 'rest_validate_request_arg', |
||
292 | ); |
||
293 | $schema = $this->get_item_schema(); |
||
294 | if ( empty( $schema['properties'] ) ) { |
||
295 | return array_merge( $param_details, $args ); |
||
296 | } |
||
297 | $contexts = array(); |
||
298 | foreach ( $schema['properties'] as $key => $attributes ) { |
||
299 | if ( ! empty( $attributes['context'] ) ) { |
||
300 | $contexts = array_merge( $contexts, $attributes['context'] ); |
||
301 | } |
||
302 | } |
||
303 | if ( ! empty( $contexts ) ) { |
||
304 | $param_details['enum'] = array_unique( $contexts ); |
||
305 | rsort( $param_details['enum'] ); |
||
306 | } |
||
307 | return array_merge( $param_details, $args ); |
||
308 | } |
||
309 | |||
310 | /** |
||
311 | * Add the values from additional fields to a data object. |
||
312 | * |
||
313 | * @param array $object |
||
314 | * @param WP_REST_Request $request |
||
315 | * @return array modified object with additional fields. |
||
316 | */ |
||
317 | protected function add_additional_fields_to_object( $object, $request ) { |
||
332 | |||
333 | /** |
||
334 | * Update the values of additional fields added to a data object. |
||
335 | * |
||
336 | * @param array $object |
||
337 | * @param WP_REST_Request $request |
||
338 | */ |
||
339 | protected function update_additional_fields_for_object( $object, $request ) { |
||
357 | |||
358 | /** |
||
359 | * Add the schema from additional fields to an schema array. |
||
360 | * |
||
361 | * The type of object is inferred from the passed schema. |
||
362 | * |
||
363 | * @param array $schema Schema array. |
||
364 | */ |
||
365 | protected function add_additional_fields_schema( $schema ) { |
||
366 | if ( empty( $schema['title'] ) ) { |
||
367 | return $schema; |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * Can't use $this->get_object_type otherwise we cause an inf loop. |
||
372 | */ |
||
373 | $object_type = $schema['title']; |
||
374 | |||
375 | $additional_fields = $this->get_additional_fields( $object_type ); |
||
376 | |||
377 | View Code Duplication | foreach ( $additional_fields as $field_name => $field_options ) { |
|
|
|||
378 | if ( ! $field_options['schema'] ) { |
||
379 | continue; |
||
380 | } |
||
381 | |||
382 | $schema['properties'][ $field_name ] = $field_options['schema']; |
||
383 | } |
||
384 | |||
385 | return $schema; |
||
386 | } |
||
387 | |||
388 | /** |
||
389 | * Get all the registered additional fields for a given object-type. |
||
390 | * |
||
391 | * @param string $object_type |
||
392 | * @return array |
||
393 | */ |
||
394 | protected function get_additional_fields( $object_type = null ) { |
||
412 | |||
413 | /** |
||
414 | * Get the object type this controller is responsible for managing. |
||
415 | * |
||
416 | * @return string |
||
417 | */ |
||
418 | protected function get_object_type() { |
||
427 | |||
428 | /** |
||
429 | * Get an array of endpoint arguments from the item schema for the controller. |
||
430 | * |
||
431 | * @param string $method HTTP method of the request. The arguments |
||
432 | * for `CREATABLE` requests are checked for required |
||
433 | * values and may fall-back to a given default, this |
||
434 | * is not done on `EDITABLE` requests. Default is |
||
435 | * WP_REST_Server::CREATABLE. |
||
436 | * @return array $endpoint_args |
||
437 | */ |
||
438 | public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CREATABLE ) { |
||
488 | |||
489 | } |
||
490 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.