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 WC_REST_Posts_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 WC_REST_Posts_Controller, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
15 | abstract class WC_REST_Posts_Controller extends WC_REST_Controller { |
||
16 | |||
17 | /** |
||
18 | * Endpoint namespace. |
||
19 | * |
||
20 | * @var string |
||
21 | */ |
||
22 | protected $namespace = 'wc/v1'; |
||
23 | |||
24 | /** |
||
25 | * Route base. |
||
26 | * |
||
27 | * @var string |
||
28 | */ |
||
29 | protected $rest_base = ''; |
||
30 | |||
31 | /** |
||
32 | * Post type. |
||
33 | * |
||
34 | * @var string |
||
35 | */ |
||
36 | protected $post_type = ''; |
||
37 | |||
38 | /** |
||
39 | * Controls visibility on frontend. |
||
40 | * |
||
41 | * @var string |
||
42 | */ |
||
43 | protected $public = false; |
||
44 | |||
45 | /** |
||
46 | * Check if a given request has access to read items. |
||
47 | * |
||
48 | * @param WP_REST_Request $request Full details about the request. |
||
49 | * @return WP_Error|boolean |
||
50 | */ |
||
51 | public function get_items_permissions_check( $request ) { |
||
52 | if ( ! wc_rest_check_post_permissions( $this->post_type, 'read' ) ) { |
||
53 | return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); |
||
54 | } |
||
55 | |||
56 | return true; |
||
57 | } |
||
58 | |||
59 | /** |
||
60 | * Check if a given request has access to create an item. |
||
61 | * |
||
62 | * @param WP_REST_Request $request Full details about the request. |
||
63 | * @return WP_Error|boolean |
||
64 | */ |
||
65 | public function create_item_permissions_check( $request ) { |
||
66 | if ( ! wc_rest_check_post_permissions( $this->post_type, 'create' ) ) { |
||
67 | return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); |
||
68 | } |
||
69 | |||
70 | return true; |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Check if a given request has access to read an item. |
||
75 | * |
||
76 | * @param WP_REST_Request $request Full details about the request. |
||
77 | * @return WP_Error|boolean |
||
78 | */ |
||
79 | View Code Duplication | public function get_item_permissions_check( $request ) { |
|
88 | |||
89 | /** |
||
90 | * Check if a given request has access to update an item. |
||
91 | * |
||
92 | * @param WP_REST_Request $request Full details about the request. |
||
93 | * @return WP_Error|boolean |
||
94 | */ |
||
95 | View Code Duplication | public function update_item_permissions_check( $request ) { |
|
104 | |||
105 | /** |
||
106 | * Check if a given request has access to delete an item. |
||
107 | * |
||
108 | * @param WP_REST_Request $request Full details about the request. |
||
109 | * @return bool|WP_Error |
||
110 | */ |
||
111 | View Code Duplication | public function delete_item_permissions_check( $request ) { |
|
120 | |||
121 | /** |
||
122 | * Check if a given request has access batch create, update and delete items. |
||
123 | * |
||
124 | * @param WP_REST_Request $request Full details about the request. |
||
125 | * @return boolean |
||
126 | */ |
||
127 | public function batch_items_permissions_check( $request ) { |
||
128 | if ( ! wc_rest_check_post_permissions( $this->post_type, 'batch' ) ) { |
||
129 | return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to manipule this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); |
||
130 | } |
||
131 | |||
132 | return true; |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Get a single item. |
||
137 | * |
||
138 | * @param WP_REST_Request $request Full details about the request. |
||
139 | * @return WP_Error|WP_REST_Response |
||
140 | */ |
||
141 | public function get_item( $request ) { |
||
158 | |||
159 | /** |
||
160 | * Create a single item. |
||
161 | * |
||
162 | * @param WP_REST_Request $request Full details about the request. |
||
163 | * @return WP_Error|WP_REST_Response |
||
164 | */ |
||
165 | public function create_item( $request ) { |
||
166 | if ( ! empty( $request['id'] ) ) { |
||
167 | return new WP_Error( "woocommerce_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'woocommerce' ), $this->post_type ), array( 'status' => 400 ) ); |
||
168 | } |
||
169 | |||
170 | $post = $this->prepare_item_for_database( $request ); |
||
171 | if ( is_wp_error( $post ) ) { |
||
172 | return $post; |
||
173 | } |
||
174 | |||
175 | $post->post_type = $this->post_type; |
||
176 | $post_id = wp_insert_post( $post, true ); |
||
177 | |||
178 | View Code Duplication | if ( is_wp_error( $post_id ) ) { |
|
179 | |||
180 | if ( in_array( $post_id->get_error_code(), array( 'db_insert_error' ) ) ) { |
||
181 | $post_id->add_data( array( 'status' => 500 ) ); |
||
182 | } else { |
||
183 | $post_id->add_data( array( 'status' => 400 ) ); |
||
184 | } |
||
185 | return $post_id; |
||
186 | } |
||
187 | $post->ID = $post_id; |
||
188 | $post = get_post( $post_id ); |
||
189 | |||
190 | $this->update_additional_fields_for_object( $post, $request ); |
||
191 | |||
192 | // Add meta fields. |
||
193 | $meta_fields = $this->add_post_meta_fields( $post, $request ); |
||
194 | if ( is_wp_error( $meta_fields ) ) { |
||
195 | // Remove post. |
||
196 | $this->delete_post( $post ); |
||
197 | |||
198 | return $meta_fields; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Fires after a single item is created or updated via the REST API. |
||
203 | * |
||
204 | * @param object $post Inserted object (not a WP_Post object). |
||
205 | * @param WP_REST_Request $request Request object. |
||
206 | * @param boolean $creating True when creating item, false when updating. |
||
207 | */ |
||
208 | do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, true ); |
||
209 | |||
210 | $request->set_param( 'context', 'edit' ); |
||
211 | $response = $this->prepare_item_for_response( $post, $request ); |
||
212 | $response = rest_ensure_response( $response ); |
||
213 | $response->set_status( 201 ); |
||
214 | $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) ); |
||
215 | |||
216 | return $response; |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * Add post meta fields. |
||
221 | * |
||
222 | * @param WP_Post $post |
||
223 | * @param WP_REST_Request $request |
||
224 | * @return bool|WP_Error |
||
225 | */ |
||
226 | protected function add_post_meta_fields( $post, $request ) { |
||
229 | |||
230 | /** |
||
231 | * Delete post. |
||
232 | * |
||
233 | * @param WP_Post $post |
||
234 | */ |
||
235 | protected function delete_post( $post ) { |
||
238 | |||
239 | /** |
||
240 | * Update a single post. |
||
241 | * |
||
242 | * @param WP_REST_Request $request Full details about the request. |
||
243 | * @return WP_Error|WP_REST_Response |
||
244 | */ |
||
245 | public function update_item( $request ) { |
||
246 | $id = (int) $request['id']; |
||
247 | $post = get_post( $id ); |
||
248 | |||
249 | if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { |
||
250 | return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'ID is invalid.', 'woocommerce' ), array( 'status' => 400 ) ); |
||
251 | } |
||
252 | |||
253 | $post = $this->prepare_item_for_database( $request ); |
||
254 | if ( is_wp_error( $post ) ) { |
||
255 | return $post; |
||
256 | } |
||
257 | // Convert the post object to an array, otherwise wp_update_post will expect non-escaped input. |
||
258 | $post_id = wp_update_post( (array) $post, true ); |
||
259 | View Code Duplication | if ( is_wp_error( $post_id ) ) { |
|
260 | if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ) ) ) { |
||
261 | $post_id->add_data( array( 'status' => 500 ) ); |
||
262 | } else { |
||
263 | $post_id->add_data( array( 'status' => 400 ) ); |
||
264 | } |
||
265 | return $post_id; |
||
266 | } |
||
267 | |||
268 | $post = get_post( $post_id ); |
||
269 | $this->update_additional_fields_for_object( $post, $request ); |
||
270 | |||
271 | // Update meta fields. |
||
272 | $meta_fields = $this->update_post_meta_fields( $post, $request ); |
||
273 | if ( is_wp_error( $meta_fields ) ) { |
||
274 | return $meta_fields; |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * Fires after a single item is created or updated via the REST API. |
||
279 | * |
||
280 | * @param object $post Inserted object (not a WP_Post object). |
||
281 | * @param WP_REST_Request $request Request object. |
||
282 | * @param boolean $creating True when creating item, false when updating. |
||
283 | */ |
||
284 | do_action( "woocommerce_rest_insert_{$this->post_type}", $post, $request, false ); |
||
285 | |||
286 | $request->set_param( 'context', 'edit' ); |
||
287 | $response = $this->prepare_item_for_response( $post, $request ); |
||
288 | return rest_ensure_response( $response ); |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Get a collection of posts. |
||
293 | * |
||
294 | * @param WP_REST_Request $request Full details about the request. |
||
295 | * @return WP_Error|WP_REST_Response |
||
296 | */ |
||
297 | public function get_items( $request ) { |
||
396 | |||
397 | /** |
||
398 | * Delete a single item. |
||
399 | * |
||
400 | * @param WP_REST_Request $request Full details about the request. |
||
401 | * @return WP_REST_Response|WP_Error |
||
402 | */ |
||
403 | public function delete_item( $request ) { |
||
465 | |||
466 | /** |
||
467 | * Prepare links for the request. |
||
468 | * |
||
469 | * @param WP_Post $post Post object. |
||
470 | * @return array Links for the given post. |
||
471 | */ |
||
472 | View Code Duplication | protected function prepare_links( $post ) { |
|
484 | |||
485 | /** |
||
486 | * Determine the allowed query_vars for a get_items() response and |
||
487 | * prepare for WP_Query. |
||
488 | * |
||
489 | * @param array $prepared_args |
||
490 | * @param WP_REST_Request $request |
||
491 | * @return array $query_args |
||
492 | */ |
||
493 | protected function prepare_items_query( $prepared_args = array(), $request = null ) { |
||
519 | |||
520 | /** |
||
521 | * Get all the WP Query vars that are allowed for the API request. |
||
522 | * |
||
523 | * @return array |
||
524 | */ |
||
525 | protected function get_allowed_query_vars() { |
||
588 | |||
589 | /** |
||
590 | * Get the query params for collections of attachments. |
||
591 | * |
||
592 | * @return array |
||
593 | */ |
||
594 | public function get_collection_params() { |
||
595 | $params = parent::get_collection_params(); |
||
596 | |||
597 | $params['context']['default'] = 'view'; |
||
598 | |||
599 | $params['after'] = array( |
||
600 | 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'woocommerce' ), |
||
601 | 'type' => 'string', |
||
602 | 'format' => 'date-time', |
||
603 | 'validate_callback' => 'rest_validate_request_arg', |
||
604 | ); |
||
605 | $params['before'] = array( |
||
606 | 'description' => __( 'Limit response to resources published before a given ISO8601 compliant date.', 'woocommerce' ), |
||
607 | 'type' => 'string', |
||
608 | 'format' => 'date-time', |
||
609 | 'validate_callback' => 'rest_validate_request_arg', |
||
610 | ); |
||
611 | $params['exclude'] = array( |
||
612 | 'description' => __( 'Ensure result set excludes specific ids.', 'woocommerce' ), |
||
613 | 'type' => 'array', |
||
614 | 'default' => array(), |
||
615 | 'sanitize_callback' => 'wp_parse_id_list', |
||
616 | ); |
||
617 | $params['include'] = array( |
||
618 | 'description' => __( 'Limit result set to specific ids.', 'woocommerce' ), |
||
619 | 'type' => 'array', |
||
620 | 'default' => array(), |
||
621 | 'sanitize_callback' => 'wp_parse_id_list', |
||
622 | ); |
||
623 | $params['offset'] = array( |
||
624 | 'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ), |
||
625 | 'type' => 'integer', |
||
626 | 'sanitize_callback' => 'absint', |
||
627 | 'validate_callback' => 'rest_validate_request_arg', |
||
628 | ); |
||
629 | $params['order'] = array( |
||
630 | 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ), |
||
631 | 'type' => 'string', |
||
632 | 'default' => 'desc', |
||
633 | 'enum' => array( 'asc', 'desc' ), |
||
634 | 'validate_callback' => 'rest_validate_request_arg', |
||
635 | ); |
||
636 | $params['orderby'] = array( |
||
637 | 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ), |
||
638 | 'type' => 'string', |
||
639 | 'default' => 'date', |
||
640 | 'enum' => array( |
||
641 | 'date', |
||
642 | 'id', |
||
643 | 'include', |
||
644 | 'title', |
||
645 | 'slug', |
||
646 | ), |
||
647 | 'validate_callback' => 'rest_validate_request_arg', |
||
648 | ); |
||
649 | |||
650 | $post_type_obj = get_post_type_object( $this->post_type ); |
||
651 | if ( $post_type_obj->hierarchical ) { |
||
652 | $params['parent'] = array( |
||
653 | 'description' => __( 'Limit result set to those of particular parent ids.', 'woocommerce' ), |
||
654 | 'type' => 'array', |
||
655 | 'sanitize_callback' => 'wp_parse_id_list', |
||
656 | 'default' => array(), |
||
657 | ); |
||
658 | $params['parent_exclude'] = array( |
||
659 | 'description' => __( 'Limit result set to all items except those of a particular parent id.', 'woocommerce' ), |
||
660 | 'type' => 'array', |
||
661 | 'sanitize_callback' => 'wp_parse_id_list', |
||
662 | 'default' => array(), |
||
663 | ); |
||
664 | } |
||
665 | |||
666 | $params['filter'] = array( |
||
667 | 'description' => __( 'Use WP Query arguments to modify the response; private query vars require appropriate authorization.', 'woocommerce' ), |
||
668 | ); |
||
669 | |||
670 | return $params; |
||
671 | } |
||
672 | |||
673 | /** |
||
674 | * Update post meta fields. |
||
675 | * |
||
676 | * @param WP_Post $post |
||
677 | * @param WP_REST_Request $request |
||
678 | * @return bool|WP_Error |
||
679 | */ |
||
680 | protected function update_post_meta_fields( $post, $request ) { |
||
683 | } |
||
684 |
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.