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_Terms_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_Terms_Controller, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
15 | abstract class WC_REST_Terms_Controller extends WC_REST_Controller { |
||
16 | |||
17 | /** |
||
18 | * Route base. |
||
19 | * |
||
20 | * @var string |
||
21 | */ |
||
22 | protected $rest_base = ''; |
||
23 | |||
24 | /** |
||
25 | * Taxonomy. |
||
26 | * |
||
27 | * @var string |
||
28 | */ |
||
29 | protected $taxonomy = ''; |
||
30 | |||
31 | /** |
||
32 | * Register the routes for terms. |
||
33 | */ |
||
34 | public function register_routes() { |
||
35 | register_rest_route( $this->namespace, '/' . $this->rest_base, array( |
||
36 | array( |
||
37 | 'methods' => WP_REST_Server::READABLE, |
||
38 | 'callback' => array( $this, 'get_items' ), |
||
39 | 'permission_callback' => array( $this, 'get_items_permissions_check' ), |
||
40 | 'args' => $this->get_collection_params(), |
||
41 | ), |
||
42 | array( |
||
43 | 'methods' => WP_REST_Server::CREATABLE, |
||
44 | 'callback' => array( $this, 'create_item' ), |
||
45 | 'permission_callback' => array( $this, 'create_item_permissions_check' ), |
||
46 | 'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array( |
||
47 | 'name' => array( |
||
48 | 'required' => true, |
||
49 | ), |
||
50 | ) ), |
||
51 | ), |
||
52 | 'schema' => array( $this, 'get_public_item_schema' ), |
||
53 | )); |
||
54 | |||
55 | register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array( |
||
56 | array( |
||
57 | 'methods' => WP_REST_Server::READABLE, |
||
58 | 'callback' => array( $this, 'get_item' ), |
||
59 | 'permission_callback' => array( $this, 'get_item_permissions_check' ), |
||
60 | 'args' => array( |
||
61 | 'context' => $this->get_context_param( array( 'default' => 'view' ) ), |
||
62 | ), |
||
63 | ), |
||
64 | array( |
||
65 | 'methods' => WP_REST_Server::EDITABLE, |
||
66 | 'callback' => array( $this, 'update_item' ), |
||
67 | 'permission_callback' => array( $this, 'update_item_permissions_check' ), |
||
68 | 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), |
||
69 | ), |
||
70 | array( |
||
71 | 'methods' => WP_REST_Server::DELETABLE, |
||
72 | 'callback' => array( $this, 'delete_item' ), |
||
73 | 'permission_callback' => array( $this, 'delete_item_permissions_check' ), |
||
74 | 'args' => array( |
||
75 | 'force' => array( |
||
76 | 'default' => false, |
||
77 | 'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ), |
||
78 | ), |
||
79 | ), |
||
80 | ), |
||
81 | 'schema' => array( $this, 'get_public_item_schema' ), |
||
82 | ) ); |
||
83 | |||
84 | register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array( |
||
85 | array( |
||
86 | 'methods' => WP_REST_Server::EDITABLE, |
||
87 | 'callback' => array( $this, 'batch_items' ), |
||
88 | 'permission_callback' => array( $this, 'batch_items_permissions_check' ), |
||
89 | 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), |
||
90 | ), |
||
91 | 'schema' => array( $this, 'get_public_batch_schema' ), |
||
92 | ) ); |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Check if a given request has access to read the terms. |
||
97 | * |
||
98 | * @param WP_REST_Request $request Full details about the request. |
||
99 | * @return WP_Error|boolean |
||
100 | */ |
||
101 | View Code Duplication | public function get_items_permissions_check( $request ) { |
|
113 | |||
114 | /** |
||
115 | * Check if a given request has access to create a term. |
||
116 | * |
||
117 | * @param WP_REST_Request $request Full details about the request. |
||
118 | * @return WP_Error|boolean |
||
119 | */ |
||
120 | View Code Duplication | public function create_item_permissions_check( $request ) { |
|
132 | |||
133 | /** |
||
134 | * Check if a given request has access to read a term. |
||
135 | * |
||
136 | * @param WP_REST_Request $request Full details about the request. |
||
137 | * @return WP_Error|boolean |
||
138 | */ |
||
139 | View Code Duplication | public function get_item_permissions_check( $request ) { |
|
151 | |||
152 | /** |
||
153 | * Check if a given request has access to update a term. |
||
154 | * |
||
155 | * @param WP_REST_Request $request Full details about the request. |
||
156 | * @return WP_Error|boolean |
||
157 | */ |
||
158 | View Code Duplication | public function update_item_permissions_check( $request ) { |
|
170 | |||
171 | /** |
||
172 | * Check if a given request has access to delete a term. |
||
173 | * |
||
174 | * @param WP_REST_Request $request Full details about the request. |
||
175 | * @return WP_Error|boolean |
||
176 | */ |
||
177 | View Code Duplication | public function delete_item_permissions_check( $request ) { |
|
189 | |||
190 | /** |
||
191 | * Check if a given request has access batch create, update and delete items. |
||
192 | * |
||
193 | * @param WP_REST_Request $request Full details about the request. |
||
194 | * @return boolean |
||
195 | */ |
||
196 | View Code Duplication | public function batch_items_permissions_check( $request ) { |
|
197 | $permissions = $this->check_permissions( $request, 'batch' ); |
||
198 | if ( is_wp_error( $permissions ) ) { |
||
199 | return $permissions; |
||
200 | } |
||
201 | |||
202 | if ( ! $permissions ) { |
||
203 | return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to manipule this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); |
||
204 | } |
||
205 | |||
206 | return true; |
||
207 | } |
||
208 | |||
209 | /** |
||
210 | * Check permissions. |
||
211 | * |
||
212 | * @param WP_REST_Request $request Full details about the request. |
||
213 | * @param string $context Request context. |
||
214 | * @return bool|WP_Error |
||
215 | */ |
||
216 | protected function check_permissions( $request, $context = 'read' ) { |
||
236 | |||
237 | /** |
||
238 | * Get terms associated with a taxonomy. |
||
239 | * |
||
240 | * @param WP_REST_Request $request Full details about the request. |
||
241 | * @return WP_REST_Response|WP_Error |
||
242 | */ |
||
243 | public function get_items( $request ) { |
||
345 | |||
346 | /** |
||
347 | * Create a single term for a taxonomy. |
||
348 | * |
||
349 | * @param WP_REST_Request $request Full details about the request. |
||
350 | * @return WP_REST_Request|WP_Error |
||
351 | */ |
||
352 | public function create_item( $request ) { |
||
425 | |||
426 | /** |
||
427 | * Get a single term from a taxonomy. |
||
428 | * |
||
429 | * @param WP_REST_Request $request Full details about the request. |
||
430 | * @return WP_REST_Request|WP_Error |
||
431 | */ |
||
432 | public function get_item( $request ) { |
||
444 | |||
445 | /** |
||
446 | * Update a single term from a taxonomy. |
||
447 | * |
||
448 | * @param WP_REST_Request $request Full details about the request. |
||
449 | * @return WP_REST_Request|WP_Error |
||
450 | */ |
||
451 | public function update_item( $request ) { |
||
512 | |||
513 | /** |
||
514 | * Delete a single term from a taxonomy. |
||
515 | * |
||
516 | * @param WP_REST_Request $request Full details about the request. |
||
517 | * @return WP_REST_Response|WP_Error |
||
518 | */ |
||
519 | public function delete_item( $request ) { |
||
520 | $taxonomy = $this->get_taxonomy( $request ); |
||
521 | $force = isset( $request['force'] ) ? (bool) $request['force'] : false; |
||
522 | |||
523 | // We don't support trashing for this type, error out. |
||
524 | if ( ! $force ) { |
||
525 | return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Resource does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) ); |
||
526 | } |
||
527 | |||
528 | $term = get_term( (int) $request['id'], $taxonomy ); |
||
529 | $request->set_param( 'context', 'edit' ); |
||
530 | $response = $this->prepare_item_for_response( $term, $request ); |
||
531 | |||
532 | $retval = wp_delete_term( $term->term_id, $term->taxonomy ); |
||
533 | if ( ! $retval ) { |
||
534 | return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) ); |
||
535 | } |
||
536 | |||
537 | /** |
||
538 | * Fires after a single term is deleted via the REST API. |
||
539 | * |
||
540 | * @param WP_Term $term The deleted term. |
||
541 | * @param WP_REST_Response $response The response data. |
||
542 | * @param WP_REST_Request $request The request sent to the API. |
||
543 | */ |
||
544 | do_action( "woocommerce_rest_delete_{$taxonomy}", $term, $response, $request ); |
||
545 | |||
546 | return $response; |
||
547 | } |
||
548 | |||
549 | /** |
||
550 | * Prepare links for the request. |
||
551 | * |
||
552 | * @param object $term Term object. |
||
553 | * @param WP_REST_Request $request Full details about the request. |
||
554 | * @return array Links for the given term. |
||
555 | */ |
||
556 | protected function prepare_links( $term, $request ) { |
||
583 | |||
584 | /** |
||
585 | * Update term meta fields. |
||
586 | * |
||
587 | * @param WP_Term $term |
||
588 | * @param WP_REST_Request $request |
||
589 | * @return bool|WP_Error |
||
590 | */ |
||
591 | protected function update_term_meta_fields( $term, $request ) { |
||
594 | |||
595 | /** |
||
596 | * Get the terms attached to a product. |
||
597 | * |
||
598 | * This is an alternative to `get_terms()` that uses `get_the_terms()` |
||
599 | * instead, which hits the object cache. There are a few things not |
||
600 | * supported, notably `include`, `exclude`. In `self::get_items()` these |
||
601 | * are instead treated as a full query. |
||
602 | * |
||
603 | * @param array $prepared_args Arguments for `get_terms()`. |
||
604 | * @return array List of term objects. (Total count in `$this->total_terms`). |
||
605 | */ |
||
606 | protected function get_terms_for_product( $prepared_args ) { |
||
642 | |||
643 | /** |
||
644 | * Comparison function for sorting terms by a column. |
||
645 | * |
||
646 | * Uses `$this->sort_column` to determine field to sort by. |
||
647 | * |
||
648 | * @param stdClass $left Term object. |
||
649 | * @param stdClass $right Term object. |
||
650 | * @return int <0 if left is higher "priority" than right, 0 if equal, >0 if right is higher "priority" than left. |
||
651 | */ |
||
652 | protected function compare_terms( $left, $right ) { |
||
663 | |||
664 | /** |
||
665 | * Get the query params for collections |
||
666 | * |
||
667 | * @return array |
||
668 | */ |
||
669 | public function get_collection_params() { |
||
756 | |||
757 | /** |
||
758 | * Get taxonomy. |
||
759 | * |
||
760 | * @param WP_REST_Request $request Full details about the request. |
||
761 | * @return int|WP_Error |
||
762 | */ |
||
763 | protected function get_taxonomy( $request ) { |
||
778 | } |
||
779 |
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.