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_CLI_Customer 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_CLI_Customer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
11 | class WC_CLI_Customer extends WC_CLI_Command { |
||
12 | |||
13 | /** |
||
14 | * Create a customer. |
||
15 | * |
||
16 | * ## OPTIONS |
||
17 | * |
||
18 | * <email> |
||
19 | * : The email address of the customer to create. |
||
20 | * |
||
21 | * [--<field>=<value>] |
||
22 | * : Associative args for the new customer. |
||
23 | * |
||
24 | * [--porcelain] |
||
25 | * : Outputs just the new customer id. |
||
26 | * |
||
27 | * ## AVAILABLE FIELDS |
||
28 | * |
||
29 | * These fields are optionally available for create command: |
||
30 | * |
||
31 | * * username |
||
32 | * * password |
||
33 | * * first_name |
||
34 | * * last_name |
||
35 | * |
||
36 | * Billing address fields: |
||
37 | * |
||
38 | * * billing_address.first_name |
||
39 | * * billing_address.last_name |
||
40 | * * billing_address.company |
||
41 | * * billing_address.address_1 |
||
42 | * * billing_address.address_2 |
||
43 | * * billing_address.city |
||
44 | * * billing_address.state |
||
45 | * * billing_address.postcode |
||
46 | * * billing_address.country |
||
47 | * * billing_address.email |
||
48 | * * billing_address.phone |
||
49 | * |
||
50 | * Shipping address fields: |
||
51 | * |
||
52 | * * shipping_address.first_name |
||
53 | * * shipping_address.last_name |
||
54 | * * shipping_address.company |
||
55 | * * shipping_address.address_1 |
||
56 | * * shipping_address.address_2 |
||
57 | * * shipping_address.city |
||
58 | * * shipping_address.state |
||
59 | * * shipping_address.postcode |
||
60 | * * shipping_address.country |
||
61 | * |
||
62 | * ## EXAMPLES |
||
63 | * |
||
64 | * wp wc customer create [email protected] --first_name=Akeda |
||
65 | * |
||
66 | * @since 2.5.0 |
||
67 | */ |
||
68 | public function create( $args, $assoc_args ) { |
||
106 | |||
107 | /** |
||
108 | * Delete one or more customers. |
||
109 | * |
||
110 | * ## OPTIONS |
||
111 | * |
||
112 | * <customer>... |
||
113 | * : The customer ID, email, or username to delete. |
||
114 | * |
||
115 | * ## EXAMPLES |
||
116 | * |
||
117 | * wp wc customer delete 123 |
||
118 | * |
||
119 | * wp wc customer delete $(wp wc customer list --format=ids) |
||
120 | * |
||
121 | * @since 2.5.0 |
||
122 | */ |
||
123 | public function delete( $args, $assoc_args ) { |
||
143 | |||
144 | /** |
||
145 | * View customer downloads. |
||
146 | * |
||
147 | * ## OPTIONS |
||
148 | * |
||
149 | * <customer> |
||
150 | * : The customer ID, email or username. |
||
151 | * |
||
152 | * [--field=<field>] |
||
153 | * : Instead of returning the whole customer fields, returns the value of a single fields. |
||
154 | * |
||
155 | * [--fields=<fields>] |
||
156 | * : Get a specific subset of the customer's fields. |
||
157 | * |
||
158 | * [--format=<format>] |
||
159 | * : Accepted values: table, json, csv. Default: table. |
||
160 | * |
||
161 | * ## AVAILABLE FIELDS |
||
162 | * |
||
163 | * * download_id |
||
164 | * * download_name |
||
165 | * * access_expires |
||
166 | * |
||
167 | * ## EXAMPLES |
||
168 | * |
||
169 | * wp wc customer downloads 123 |
||
170 | * |
||
171 | * @since 2.5.0 |
||
172 | */ |
||
173 | public function downloads( $args, $assoc_args ) { |
||
194 | |||
195 | /** |
||
196 | * Get a customer. |
||
197 | * |
||
198 | * ## OPTIONS |
||
199 | * |
||
200 | * <customer> |
||
201 | * : Customer ID, email, or username. |
||
202 | * |
||
203 | * [--field=<field>] |
||
204 | * : Instead of returning the whole customer fields, returns the value of a single fields. |
||
205 | * |
||
206 | * [--fields=<fields>] |
||
207 | * : Get a specific subset of the customer's fields. |
||
208 | * |
||
209 | * [--format=<format>] |
||
210 | * : Accepted values: table, json, csv. Default: table. |
||
211 | * |
||
212 | * ## AVAILABLE FIELDS |
||
213 | * |
||
214 | * * id |
||
215 | |||
216 | * * first_name |
||
217 | * * last_name |
||
218 | * * created_at |
||
219 | * * username |
||
220 | * * last_order_id |
||
221 | * * last_order_date |
||
222 | * * orders_count |
||
223 | * * total_spent |
||
224 | * * avatar_url |
||
225 | * |
||
226 | * Billing address fields: |
||
227 | * |
||
228 | * * billing_address.first_name |
||
229 | * * billing_address.last_name |
||
230 | * * billing_address.company |
||
231 | * * billing_address.address_1 |
||
232 | * * billing_address.address_2 |
||
233 | * * billing_address.city |
||
234 | * * billing_address.state |
||
235 | * * billing_address.postcode |
||
236 | * * billing_address.country |
||
237 | * * billing_address.email |
||
238 | * * billing_address.phone |
||
239 | * |
||
240 | * Shipping address fields: |
||
241 | * |
||
242 | * * shipping_address.first_name |
||
243 | * * shipping_address.last_name |
||
244 | * * shipping_address.company |
||
245 | * * shipping_address.address_1 |
||
246 | * * shipping_address.address_2 |
||
247 | * * shipping_address.city |
||
248 | * * shipping_address.state |
||
249 | * * shipping_address.postcode |
||
250 | * * shipping_address.country |
||
251 | * |
||
252 | * Fields for filtering query result also available: |
||
253 | * |
||
254 | * * role Filter customers associated with certain role. |
||
255 | * * q Filter customers with search query. |
||
256 | * * created_at_min Filter customers whose registered after this date. |
||
257 | * * created_at_max Filter customers whose registered before this date. |
||
258 | * * limit The maximum returned number of results. |
||
259 | * * offset Offset the returned results. |
||
260 | * * order Accepted values: ASC and DESC. Default: DESC. |
||
261 | * * orderby Sort retrieved customers by parameter. One or more options can be passed. |
||
262 | * |
||
263 | * ## EXAMPLES |
||
264 | * |
||
265 | * wp wc customer get 123 --field=email |
||
266 | * |
||
267 | * wp wc customer get customer-login --format=json |
||
268 | * |
||
269 | * @since 2.5.0 |
||
270 | */ |
||
271 | public function get( $args, $assoc_args ) { |
||
285 | |||
286 | /** |
||
287 | * List customers. |
||
288 | * |
||
289 | * ## OPTIONS |
||
290 | * |
||
291 | * [--<field>=<value>] |
||
292 | * : Filter customer based on customer property. |
||
293 | * |
||
294 | * [--field=<field>] |
||
295 | * : Prints the value of a single field for each customer. |
||
296 | * |
||
297 | * [--fields=<fields>] |
||
298 | * : Limit the output to specific customer fields. |
||
299 | * |
||
300 | * [--format=<format>] |
||
301 | * : Acceptec values: table, csv, json, count, ids. Default: table. |
||
302 | * |
||
303 | * ## AVAILABLE FIELDS |
||
304 | * |
||
305 | * These fields will be displayed by default for each customer: |
||
306 | * |
||
307 | * * id |
||
308 | |||
309 | * * first_name |
||
310 | * * last_name |
||
311 | * * created_at |
||
312 | * |
||
313 | * These fields are optionally available: |
||
314 | * |
||
315 | * * username |
||
316 | * * last_order_id |
||
317 | * * last_order_date |
||
318 | * * orders_count |
||
319 | * * total_spent |
||
320 | * * avatar_url |
||
321 | * |
||
322 | * Billing address fields: |
||
323 | * |
||
324 | * * billing_address.first_name |
||
325 | * * billing_address.last_name |
||
326 | * * billing_address.company |
||
327 | * * billing_address.address_1 |
||
328 | * * billing_address.address_2 |
||
329 | * * billing_address.city |
||
330 | * * billing_address.state |
||
331 | * * billing_address.postcode |
||
332 | * * billing_address.country |
||
333 | * * billing_address.email |
||
334 | * * billing_address.phone |
||
335 | * |
||
336 | * Shipping address fields: |
||
337 | * |
||
338 | * * shipping_address.first_name |
||
339 | * * shipping_address.last_name |
||
340 | * * shipping_address.company |
||
341 | * * shipping_address.address_1 |
||
342 | * * shipping_address.address_2 |
||
343 | * * shipping_address.city |
||
344 | * * shipping_address.state |
||
345 | * * shipping_address.postcode |
||
346 | * * shipping_address.country |
||
347 | * |
||
348 | * Fields for filtering query result also available: |
||
349 | * |
||
350 | * * role Filter customers associated with certain role. |
||
351 | * * q Filter customers with search query. |
||
352 | * * created_at_min Filter customers whose registered after this date. |
||
353 | * * created_at_max Filter customers whose registered before this date. |
||
354 | * * limit The maximum returned number of results. |
||
355 | * * offset Offset the returned results. |
||
356 | * * order Accepted values: ASC and DESC. Default: DESC. |
||
357 | * * orderby Sort retrieved customers by parameter. One or more options can be passed. |
||
358 | * |
||
359 | * ## EXAMPLES |
||
360 | * |
||
361 | * wp wc customer list |
||
362 | * |
||
363 | * wp wc customer list --field=id |
||
364 | * |
||
365 | * wp wc customer list --fields=id,email,first_name --format=json |
||
366 | * |
||
367 | * @subcommand list |
||
368 | * @since 2.5.0 |
||
369 | */ |
||
370 | View Code Duplication | public function list_( $__, $assoc_args ) { |
|
384 | |||
385 | /** |
||
386 | * View customer orders. |
||
387 | * |
||
388 | * ## OPTIONS |
||
389 | * |
||
390 | * <customer> |
||
391 | * : The customer ID, email or username. |
||
392 | * |
||
393 | * [--field=<field>] |
||
394 | * : Instead of returning the whole customer fields, returns the value of a single fields. |
||
395 | * |
||
396 | * [--fields=<fields>] |
||
397 | * : Get a specific subset of the customer's fields. |
||
398 | * |
||
399 | * [--format=<format>] |
||
400 | * : Accepted values: table, json, csv. Default: table. |
||
401 | * |
||
402 | * ## AVAILABLE FIELDS |
||
403 | * |
||
404 | * For more fields, see: wp wc order list --help |
||
405 | * |
||
406 | * ## EXAMPLES |
||
407 | * |
||
408 | * wp wc customer orders 123 |
||
409 | * |
||
410 | * @since 2.5.0 |
||
411 | */ |
||
412 | public function orders( $args, $assoc_args ) { |
||
419 | |||
420 | /** |
||
421 | * Update one or more customers. |
||
422 | * |
||
423 | * ## OPTIONS |
||
424 | * |
||
425 | * <customer> |
||
426 | * : Customer ID, email, or username. |
||
427 | * |
||
428 | * [--<field>=<value>] |
||
429 | * : One or more fields to update. |
||
430 | * |
||
431 | * ## AVAILABLE FIELDS |
||
432 | * |
||
433 | * These fields are available for update command: |
||
434 | * |
||
435 | |||
436 | * * password |
||
437 | * * first_name |
||
438 | * * last_name |
||
439 | * |
||
440 | * Billing address fields: |
||
441 | * |
||
442 | * * billing_address.first_name |
||
443 | * * billing_address.last_name |
||
444 | * * billing_address.company |
||
445 | * * billing_address.address_1 |
||
446 | * * billing_address.address_2 |
||
447 | * * billing_address.city |
||
448 | * * billing_address.state |
||
449 | * * billing_address.postcode |
||
450 | * * billing_address.country |
||
451 | * * billing_address.email |
||
452 | * * billing_address.phone |
||
453 | * |
||
454 | * Shipping address fields: |
||
455 | * |
||
456 | * * shipping_address.first_name |
||
457 | * * shipping_address.last_name |
||
458 | * * shipping_address.company |
||
459 | * * shipping_address.address_1 |
||
460 | * * shipping_address.address_2 |
||
461 | * * shipping_address.city |
||
462 | * * shipping_address.state |
||
463 | * * shipping_address.postcode |
||
464 | * * shipping_address.country |
||
465 | * |
||
466 | * ## EXAMPLES |
||
467 | * |
||
468 | * wp wc customer update customer-login --first_name=akeda --last_name=bagus |
||
469 | * |
||
470 | * wp wc customer update [email protected] --password=new-password |
||
471 | * |
||
472 | * @since 2.5.0 |
||
473 | */ |
||
474 | public function update( $args, $assoc_args ) { |
||
475 | try { |
||
476 | $user = $this->get_user( $args[0] ); |
||
477 | $data = $this->unflatten_array( $assoc_args ); |
||
478 | $data = apply_filters( 'woocommerce_cli_update_customer_data', $data ); |
||
479 | |||
480 | // Customer email. |
||
481 | View Code Duplication | if ( isset( $data['email'] ) ) { |
|
482 | wp_update_user( array( 'ID' => $user['id'], 'user_email' => sanitize_email( $data['email'] ) ) ); |
||
483 | } |
||
484 | |||
485 | // Customer password. |
||
486 | View Code Duplication | if ( isset( $data['password'] ) ) { |
|
487 | wp_update_user( array( 'ID' => $user['id'], 'user_pass' => wc_clean( $data['password'] ) ) ); |
||
488 | } |
||
489 | |||
490 | // Update customer data. |
||
491 | $this->update_customer_data( $user['id'], $data ); |
||
492 | |||
493 | do_action( 'woocommerce_cli_update_customer', $user['id'], $data ); |
||
494 | |||
495 | WP_CLI::success( "Updated customer {$user['id']}." ); |
||
496 | } catch ( WC_CLI_Exception $e ) { |
||
497 | WP_CLI::error( $e->getMessage() ); |
||
498 | } |
||
499 | } |
||
500 | |||
501 | /** |
||
502 | * Get query args for list subcommand. |
||
503 | * |
||
504 | * @since 2.5.0 |
||
505 | * @return array |
||
506 | */ |
||
507 | protected function get_list_query_args() { |
||
513 | |||
514 | /** |
||
515 | * Get default format fields that will be used in `list` and `get` subcommands. |
||
516 | * |
||
517 | * @since 2.5.0 |
||
518 | * @return string |
||
519 | */ |
||
520 | protected function get_default_format_fields() { |
||
523 | |||
524 | /** |
||
525 | * Format users from WP_User_Query result to items in which each item contain |
||
526 | * common properties of item. |
||
527 | * |
||
528 | * @since 2.5.0 |
||
529 | * @param array $users Array of user |
||
530 | * @return array Items |
||
531 | */ |
||
532 | protected function format_users_to_items( $users ) { |
||
544 | |||
545 | /** |
||
546 | * Get user from given user ID, email, or login |
||
547 | * |
||
548 | * @throws WC_CLI_Exception |
||
549 | * |
||
550 | * @since 2.5.0 |
||
551 | * @param mixed $id_email_or_login |
||
552 | * @return array|WP_Error |
||
553 | */ |
||
554 | protected function get_user( $id_email_or_login ) { |
||
624 | |||
625 | /** |
||
626 | * Wrapper for @see get_avatar() which doesn't simply return |
||
627 | * the URL so we need to pluck it from the HTML img tag |
||
628 | * |
||
629 | * Kudos to https://github.com/WP-API/WP-API for offering a better solution |
||
630 | * |
||
631 | * @since 2.5.0 |
||
632 | * @param string $email the customer's email |
||
633 | * @return string the URL to the customer's avatar |
||
634 | */ |
||
635 | View Code Duplication | protected function get_avatar_url( $email ) { |
|
647 | |||
648 | /** |
||
649 | * Add/Update customer data. |
||
650 | * |
||
651 | * @since 2.5.0 |
||
652 | * @param int $id The customer ID |
||
653 | * @param array $data |
||
654 | */ |
||
655 | protected function update_customer_data( $id, $data ) { |
||
686 | |||
687 | /** |
||
688 | * Get customer billing address fields. |
||
689 | * |
||
690 | * @since 2.5.0 |
||
691 | * @return array |
||
692 | */ |
||
693 | View Code Duplication | protected function get_customer_billing_address_fields() { |
|
708 | |||
709 | /** |
||
710 | * Get customer shipping address fields. |
||
711 | * |
||
712 | * @since 2.5.0 |
||
713 | * @return array |
||
714 | */ |
||
715 | View Code Duplication | protected function get_customer_shipping_address_fields() { |
|
728 | |||
729 | /** |
||
730 | * Get customer download fields. |
||
731 | * |
||
732 | * @since 2.5.0 |
||
733 | * @return array |
||
734 | */ |
||
735 | protected function get_customer_download_fields() { |
||
742 | } |
||
743 |
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.