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:
1 | <?php |
||
23 | class WC_REST_Coupons_Controller extends WC_REST_Posts_Controller { |
||
24 | |||
25 | /** |
||
26 | * Endpoint namespace. |
||
27 | * |
||
28 | * @var string |
||
29 | */ |
||
30 | protected $namespace = 'wc/v1'; |
||
31 | |||
32 | /** |
||
33 | * Route base. |
||
34 | * |
||
35 | * @var string |
||
36 | */ |
||
37 | protected $rest_base = 'coupons'; |
||
38 | |||
39 | /** |
||
40 | * Post type. |
||
41 | * |
||
42 | * @var string |
||
43 | */ |
||
44 | protected $post_type = 'shop_coupon'; |
||
45 | |||
46 | /** |
||
47 | * Order refunds actions. |
||
48 | */ |
||
49 | public function __construct() { |
||
52 | |||
53 | /** |
||
54 | * Register the routes for coupons. |
||
55 | */ |
||
56 | View Code Duplication | public function register_routes() { |
|
116 | |||
117 | /** |
||
118 | * Query args. |
||
119 | * |
||
120 | * @param array $args |
||
121 | * @param WP_REST_Request $request |
||
122 | * @return array |
||
123 | */ |
||
124 | public function query_args( $args, $request ) { |
||
125 | global $wpdb; |
||
126 | |||
127 | if ( ! empty( $request['code'] ) ) { |
||
128 | $id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->posts WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish'", $request['code'] ) ); |
||
129 | $args['post__in'] = array( $id ); |
||
130 | } |
||
131 | |||
132 | return $args; |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Prepare a single coupon output for response. |
||
137 | * |
||
138 | * @param WP_Post $post Post object. |
||
139 | * @param WP_REST_Request $request Request object. |
||
140 | * @return WP_REST_Response $data |
||
141 | */ |
||
142 | public function prepare_item_for_response( $post, $request ) { |
||
143 | $code = wc_get_coupon_code_by_id( $post->ID ); |
||
144 | $coupon = new WC_Coupon( $code ); |
||
145 | $data = $coupon->get_data(); |
||
146 | $format_decimal = array( 'amount', 'minimum_amount', 'maximum_amount' ); |
||
147 | $format_date = array( 'date_created', 'date_modified', 'date_expires' ); |
||
148 | |||
149 | // Format decimal values. |
||
150 | foreach ( $format_decimal as $key ) { |
||
151 | $data[ $key ] = wc_format_decimal( $data[ $key ], 2 ); |
||
152 | } |
||
153 | |||
154 | // Format date values. |
||
155 | View Code Duplication | foreach ( $format_date as $key ) { |
|
156 | $data[ $key ] = $data[ $key ] ? wc_rest_prepare_date_response( get_gmt_from_date( date( 'Y-m-d H:i:s', $data[ $key ] ) ) ) : false; |
||
157 | } |
||
158 | |||
159 | $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; |
||
160 | $data = $this->add_additional_fields_to_object( $data, $request ); |
||
161 | $data = $this->filter_response_by_context( $data, $context ); |
||
162 | $response = rest_ensure_response( $data ); |
||
163 | $response->add_links( $this->prepare_links( $post ) ); |
||
164 | |||
165 | /** |
||
166 | * Filter the data for a response. |
||
167 | * |
||
168 | * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being |
||
169 | * prepared for the response. |
||
170 | * |
||
171 | * @param WP_REST_Response $response The response object. |
||
172 | * @param WP_Post $post Post object. |
||
173 | * @param WP_REST_Request $request Request object. |
||
174 | */ |
||
175 | return apply_filters( "woocommerce_rest_prepare_{$this->post_type}", $response, $post, $request ); |
||
176 | } |
||
177 | |||
178 | /** |
||
179 | * Only reutrn writeable props from schema. |
||
180 | * @param array $schema |
||
181 | * @return bool |
||
182 | */ |
||
183 | protected function filter_writable_props( $schema ) { |
||
184 | return empty( $schema['readonly'] ); |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * Prepare a single coupon for create or update. |
||
189 | * |
||
190 | * @param WP_REST_Request $request Request object. |
||
191 | * @return WP_Error|stdClass $data Post object. |
||
192 | */ |
||
193 | protected function prepare_item_for_database( $request ) { |
||
194 | $id = isset( $request['id'] ) ? absint( $request['id'] ) : 0; |
||
195 | $coupon = new WC_Coupon( $id ); |
||
196 | $schema = $this->get_item_schema(); |
||
197 | $data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) ); |
||
198 | |||
199 | // Validate required POST fields. |
||
200 | if ( 'POST' === $request->get_method() && 0 === $coupon->get_id() ) { |
||
201 | if ( empty( $request['code'] ) ) { |
||
202 | return new WP_Error( 'woocommerce_rest_empty_coupon_code', sprintf( __( 'The coupon code cannot be empty.', 'woocommerce' ), 'code' ), array( 'status' => 400 ) ); |
||
203 | } |
||
204 | } |
||
205 | |||
206 | // Handle all writable props |
||
207 | foreach ( $data_keys as $key ) { |
||
208 | $value = $request[ $key ]; |
||
209 | |||
210 | if ( ! is_null( $value ) ) { |
||
211 | switch ( $key ) { |
||
212 | case 'code' : |
||
213 | $coupon_code = apply_filters( 'woocommerce_coupon_code', $value ); |
||
214 | $id = $coupon->get_id() ? $coupon->get_id() : 0; |
||
215 | |||
216 | // Check for duplicate coupon codes. |
||
217 | $coupon_found = $wpdb->get_var( $wpdb->prepare( " |
||
218 | SELECT $wpdb->posts.ID |
||
219 | FROM $wpdb->posts |
||
220 | WHERE $wpdb->posts.post_type = 'shop_coupon' |
||
221 | AND $wpdb->posts.post_status = 'publish' |
||
222 | AND $wpdb->posts.post_title = %s |
||
223 | AND $wpdb->posts.ID != %s |
||
224 | ", $coupon_code, $id ) ); |
||
225 | |||
226 | if ( $coupon_found ) { |
||
227 | return new WP_Error( 'woocommerce_rest_coupon_code_already_exists', __( 'The coupon code already exists', 'woocommerce' ), array( 'status' => 400 ) ); |
||
228 | } |
||
229 | |||
230 | $coupon->set_code( $coupon_code ); |
||
231 | break; |
||
232 | View Code Duplication | case 'meta_data' : |
|
233 | if ( is_array( $value ) ) { |
||
234 | foreach ( $value as $meta ) { |
||
235 | $coupon->update_meta_data( $meta['key'], $meta['value'], $meta['id'] ); |
||
236 | } |
||
237 | } |
||
238 | break; |
||
239 | case 'description' : |
||
240 | $coupon->set_description( wp_filter_post_kses( $value ) ); |
||
241 | break; |
||
242 | default : |
||
243 | if ( is_callable( array( $coupon, "set_{$key}" ) ) ) { |
||
244 | $coupon->{"set_{$key}"}( $value ); |
||
245 | } |
||
246 | break; |
||
247 | } |
||
248 | } |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * Filter the query_vars used in `get_items` for the constructed query. |
||
253 | * |
||
254 | * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being |
||
255 | * prepared for insertion. |
||
256 | * |
||
257 | * @param WC_Coupon $coupon The coupon object. |
||
258 | * @param WP_REST_Request $request Request object. |
||
259 | */ |
||
260 | return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}", $coupon, $request ); |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Create a single item. |
||
265 | * |
||
266 | * @param WP_REST_Request $request Full details about the request. |
||
267 | * @return WP_Error|WP_REST_Response |
||
268 | */ |
||
269 | View Code Duplication | public function create_item( $request ) { |
|
300 | |||
301 | /** |
||
302 | * Update a single coupon. |
||
303 | * |
||
304 | * @param WP_REST_Request $request Full details about the request. |
||
305 | * @return WP_Error|WP_REST_Response |
||
306 | */ |
||
307 | View Code Duplication | public function update_item( $request ) { |
|
339 | |||
340 | /** |
||
341 | * Saves a coupon to the database. |
||
342 | */ |
||
343 | public function save_coupon( $request ) { |
||
354 | |||
355 | /** |
||
356 | * Get the Coupon's schema, conforming to JSON Schema. |
||
357 | * |
||
358 | * @return array |
||
359 | */ |
||
360 | public function get_item_schema() { |
||
495 | |||
496 | /** |
||
497 | * Get the query params for collections of attachments. |
||
498 | * |
||
499 | * @return array |
||
500 | */ |
||
501 | View Code Duplication | public function get_collection_params() { |
|
511 | } |
||
512 |
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.