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 FieldsBuilder 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 FieldsBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
8 | class FieldsBuilder extends Builder implements NamedBuilder |
||
9 | { |
||
10 | /** |
||
11 | * Field Group Configuration |
||
12 | * @var array |
||
13 | */ |
||
14 | protected $config = []; |
||
15 | |||
16 | /** |
||
17 | * Manages the Field Configurations |
||
18 | * @var FieldManager |
||
19 | */ |
||
20 | protected $fieldManager; |
||
21 | |||
22 | /** |
||
23 | * Location configuration for Field Group |
||
24 | * @var LocationBuilder |
||
25 | */ |
||
26 | protected $location = null; |
||
27 | |||
28 | /** |
||
29 | * Field Group Name |
||
30 | * @var string |
||
31 | */ |
||
32 | protected $name; |
||
33 | |||
34 | /** |
||
35 | * @param string $name Field Group name |
||
36 | * @param array $groupConfig Field Group configuration |
||
37 | */ |
||
38 | public function __construct($name, $groupConfig = []) |
||
47 | |||
48 | /** |
||
49 | * Set a value for a particular key in the group config |
||
50 | * @param string $key |
||
51 | * @param mixed $value |
||
52 | */ |
||
53 | public function setGroupConfig($key, $value) |
||
59 | |||
60 | /** |
||
61 | * @return string |
||
62 | */ |
||
63 | public function getName() |
||
67 | |||
68 | /** |
||
69 | * Namespace a group key |
||
70 | * Append the namespace 'group' before the set key. |
||
71 | * |
||
72 | * @param string $key Field Key |
||
73 | * @return string Field Key |
||
74 | */ |
||
75 | private function namespaceGroupKey($key) |
||
82 | |||
83 | /** |
||
84 | * Build the final config array. Build any other builders that may exist |
||
85 | * in the config. |
||
86 | * @return array final field config |
||
87 | */ |
||
88 | public function build() |
||
96 | |||
97 | /** |
||
98 | * Return a fields config array |
||
99 | * @return array |
||
100 | */ |
||
101 | private function buildFields() |
||
109 | |||
110 | /** |
||
111 | * Apply field transforms |
||
112 | * @param array $fields |
||
113 | * @return array Transformed fields config |
||
114 | */ |
||
115 | private function transformFields($fields) |
||
125 | |||
126 | /** |
||
127 | * Return a locations config array |
||
128 | * @return array |
||
129 | */ |
||
130 | private function buildLocation() |
||
135 | |||
136 | /** |
||
137 | * Add multiple fields either via an array or from another builder |
||
138 | * @param mixed $fields array of fields or a FieldBuilder |
||
139 | * @return $this |
||
140 | */ |
||
141 | public function addFields($fields) |
||
154 | |||
155 | /** |
||
156 | * Add field to field group |
||
157 | * @param string $name field name |
||
158 | * @param array $args field options |
||
159 | * @throws FieldNameCollisionException if name already exists. |
||
160 | * @return $this |
||
161 | */ |
||
162 | public function addField($name, $args = []) |
||
173 | |||
174 | /** |
||
175 | * Add a field of a specific type |
||
176 | * @param string $name |
||
177 | * @param string $type |
||
178 | * @param array $args field configuration |
||
179 | * @return $this |
||
180 | */ |
||
181 | protected function addFieldType($name, $type, $args = []) |
||
187 | |||
188 | /** |
||
189 | * @param string $name |
||
190 | * @param array $args field configuration |
||
191 | * @return $this |
||
192 | */ |
||
193 | public function addText($name, $args = []) |
||
197 | |||
198 | /** |
||
199 | * @param string $name |
||
200 | * @param array $args field configuration |
||
201 | * @return $this |
||
202 | */ |
||
203 | public function addTextarea($name, $args = []) |
||
207 | |||
208 | /** |
||
209 | * @param string $name |
||
210 | * @param array $args field configuration |
||
211 | * @return $this |
||
212 | */ |
||
213 | public function addNumber($name, $args = []) |
||
217 | |||
218 | /** |
||
219 | * @param string $name |
||
220 | * @param array $args field configuration |
||
221 | * @return $this |
||
222 | */ |
||
223 | public function addEmail($name, $args = []) |
||
227 | |||
228 | /** |
||
229 | * @param string $name |
||
230 | * @param array $args field configuration |
||
231 | * @return $this |
||
232 | */ |
||
233 | public function addUrl($name, $args = []) |
||
237 | |||
238 | /** |
||
239 | * @param string $name |
||
240 | * @param array $args field configuration |
||
241 | * @return $this |
||
242 | */ |
||
243 | public function addPassword($name, $args = []) |
||
247 | |||
248 | /** |
||
249 | * @param string $name |
||
250 | * @param array $args field configuration |
||
251 | * @return $this |
||
252 | */ |
||
253 | public function addWysiwyg($name, $args = []) |
||
257 | |||
258 | /** |
||
259 | * @param string $name |
||
260 | * @param array $args field configuration |
||
261 | * @return $this |
||
262 | */ |
||
263 | public function addOembed($name, $args = []) |
||
267 | |||
268 | /** |
||
269 | * @param string $name |
||
270 | * @param array $args field configuration |
||
271 | * @return $this |
||
272 | */ |
||
273 | public function addImage($name, $args = []) |
||
277 | |||
278 | /** |
||
279 | * @param string $name |
||
280 | * @param array $args field configuration |
||
281 | * @return $this |
||
282 | */ |
||
283 | public function addFile($name, $args = []) |
||
287 | |||
288 | /** |
||
289 | * @param string $name |
||
290 | * @param array $args field configuration |
||
291 | * @return $this |
||
292 | */ |
||
293 | public function addGallery($name, $args = []) |
||
297 | |||
298 | /** |
||
299 | * @param string $name |
||
300 | * @param array $args field configuration |
||
301 | * @return $this |
||
302 | */ |
||
303 | public function addTrueFalse($name, $args = []) |
||
307 | |||
308 | /** |
||
309 | * @param string $name |
||
310 | * @param array $args field configuration |
||
311 | * @return $this |
||
312 | */ |
||
313 | public function addSelect($name, $args = []) |
||
317 | |||
318 | /** |
||
319 | * @param string $name |
||
320 | * @param array $args field configuration |
||
321 | * @return $this |
||
322 | */ |
||
323 | public function addRadio($name, $args = []) |
||
327 | |||
328 | /** |
||
329 | * @param string $name |
||
330 | * @param array $args field configuration |
||
331 | * @return $this |
||
332 | */ |
||
333 | public function addCheckbox($name, $args = []) |
||
337 | |||
338 | /** |
||
339 | * @param string $name |
||
340 | * @param array $args field configuration |
||
341 | * @return $this |
||
342 | */ |
||
343 | public function addPostObject($name, $args = []) |
||
347 | |||
348 | /** |
||
349 | * @param string $name |
||
350 | * @param array $args field configuration |
||
351 | * @return $this |
||
352 | */ |
||
353 | public function addPostLink($name, $args = []) |
||
357 | |||
358 | /** |
||
359 | * @param string $name |
||
360 | * @param array $args field configuration |
||
361 | * @return $this |
||
362 | */ |
||
363 | public function addRelationship($name, $args = []) |
||
367 | |||
368 | /** |
||
369 | * @param string $name |
||
370 | * @param array $args field configuration |
||
371 | * @return $this |
||
372 | */ |
||
373 | public function addTaxonomy($name, $args = []) |
||
377 | |||
378 | /** |
||
379 | * @param string $name |
||
380 | * @param array $args field configuration |
||
381 | * @return $this |
||
382 | */ |
||
383 | public function addUser($name, $args = []) |
||
387 | |||
388 | /** |
||
389 | * @param string $name |
||
390 | * @param array $args field configuration |
||
391 | * @return $this |
||
392 | */ |
||
393 | public function addDatePicker($name, $args = []) |
||
397 | |||
398 | /** |
||
399 | * @param string $name |
||
400 | * @param array $args field configuration |
||
401 | * @return $this |
||
402 | */ |
||
403 | public function addTimePicker($name, $args = []) |
||
407 | |||
408 | /** |
||
409 | * @param string $name |
||
410 | * @param array $args field configuration |
||
411 | * @return $this |
||
412 | */ |
||
413 | public function addDateTimePicker($name, $args = []) |
||
417 | |||
418 | /** |
||
419 | * @param string $name |
||
420 | * @param array $args field configuration |
||
421 | * @return $this |
||
422 | */ |
||
423 | public function addColorPicker($name, $args = []) |
||
427 | |||
428 | /** |
||
429 | * All fields added after will appear under this tab, until another tab |
||
430 | * is added. |
||
431 | * @param string $label Tab label |
||
432 | * @param array $args field configuration |
||
433 | * @return $this |
||
434 | */ |
||
435 | View Code Duplication | public function addTab($label, $args = []) |
|
444 | |||
445 | /** |
||
446 | * Configs the tab as an endpoint tab. New tabs will start on another row. |
||
447 | * @param int $value boolean 1 or 0 |
||
448 | * @return $this |
||
449 | */ |
||
450 | public function endpoint($value = 1) |
||
454 | |||
455 | /** |
||
456 | * Addes a message field |
||
457 | * @param string $label |
||
458 | * @param string $message |
||
459 | * @param array $args field configuration |
||
460 | * @return $this |
||
461 | */ |
||
462 | View Code Duplication | public function addMessage($label, $message, $args = []) |
|
472 | |||
473 | /** |
||
474 | * Add a repeater field. Any fields added after will be added to the repeater |
||
475 | * until `endRepeater` is called. |
||
476 | * @param string $name |
||
477 | * @param array $args field configuration |
||
478 | * @return RepeaterBuilder |
||
479 | */ |
||
480 | public function addRepeater($name, $args = []) |
||
488 | |||
489 | /** |
||
490 | * Add a flexible content field. Once adding a layout with `addLayout`, |
||
491 | * any fields added after will be added to that layout until another |
||
492 | * `addLayout` call is made, or until `endFlexibleContent` is called. |
||
493 | * @param string $name |
||
494 | * @param array $args field configuration |
||
495 | * @return FlexibleContentBuilder |
||
496 | */ |
||
497 | public function addFlexibleContent($name, $args = []) |
||
505 | |||
506 | /** |
||
507 | * Add a choice to the previously added radio, select or checkbox field |
||
508 | * @param string $choice |
||
509 | * @param string $label By default the value of $choice will appear next |
||
510 | * to the field. Optionally pass in a manual value for the label. |
||
511 | * @return $this |
||
512 | */ |
||
513 | public function addChoice($choice, $label = null) |
||
525 | |||
526 | /** |
||
527 | * Add a number of choices to a radio, select or checkbox field at once. |
||
528 | * Each argument will be a choice. Pass in an array of format ['name' => 'label'] |
||
529 | * to specifiy a manually set label. |
||
530 | * @return $this |
||
531 | */ |
||
532 | public function addChoices() |
||
546 | |||
547 | /** |
||
548 | * Add a conditional logic statement that will determine if the last added |
||
549 | * field will display or not. You can add `or` or `and` calls after |
||
550 | * to build complex logic. Any other function call will return you to the |
||
551 | * parentContext. |
||
552 | * @param string $name Dependent field name |
||
553 | * (choice type: radio, checkbox, select, trueFalse) |
||
554 | * @param string $operator ==, != |
||
555 | * @param string $value 1 or choice value |
||
556 | * @return ConditionalBuilder |
||
557 | */ |
||
558 | public function conditional($name, $operator, $value) |
||
569 | |||
570 | /** |
||
571 | * @return FieldManager |
||
572 | */ |
||
573 | protected function getFieldManager() |
||
577 | |||
578 | /** |
||
579 | * @return array |
||
580 | */ |
||
581 | public function getFields() |
||
585 | |||
586 | /** |
||
587 | * @param string $name [description] |
||
588 | * @return array|Builder |
||
589 | */ |
||
590 | public function getField($name) |
||
594 | |||
595 | /** |
||
596 | * Modify an already defined field |
||
597 | * @param string $name Name of the field |
||
598 | * @param array|\Closure $modify Array of field configs or a closure that accepts |
||
599 | * a FieldsBuilder and returns a FieldsBuilder. |
||
600 | * @throws ModifyFieldReturnTypeException if $modify is a closure and doesn't |
||
601 | * return a FieldsBuilder. |
||
602 | * @throws FieldNotFoundException if the field name doesn't exist. |
||
603 | * @return $this |
||
604 | */ |
||
605 | public function modifyField($name, $modify) |
||
635 | |||
636 | /** |
||
637 | * Remove a field by name |
||
638 | * @param string $name Field to remove |
||
639 | * @return $this |
||
640 | */ |
||
641 | public function removeField($name) |
||
647 | |||
648 | /** |
||
649 | * Set the default value of previously added field |
||
650 | * @param string $value |
||
651 | * @return $this |
||
652 | */ |
||
653 | public function defaultValue($value) |
||
657 | |||
658 | /** |
||
659 | * Mark the previously added field as required |
||
660 | * @param bool $value |
||
661 | * @return $this |
||
662 | */ |
||
663 | public function required($value = true) |
||
667 | |||
668 | /** |
||
669 | * Add instructions for the previously added field |
||
670 | * @param string $value |
||
671 | * @return $this |
||
672 | */ |
||
673 | public function instructions($value) |
||
677 | |||
678 | /** |
||
679 | * Set a configuration by key on the previously added field |
||
680 | * @param string $key |
||
681 | * @param string $value |
||
682 | * @return $this |
||
683 | */ |
||
684 | public function setConfig($key, $value) |
||
692 | |||
693 | /** |
||
694 | * Set the location of the field group. See |
||
695 | * https://github.com/StoutLogic/acf-builder/wiki/location and |
||
696 | * https://www.advancedcustomfields.com/resources/custom-location-rules/ |
||
697 | * for more details. |
||
698 | * @param string $param |
||
699 | * @param string $operator |
||
700 | * @param string $value |
||
701 | * @return LocationBuilder |
||
702 | */ |
||
703 | public function setLocation($param, $operator, $value) |
||
714 | |||
715 | /** |
||
716 | * @return LocationBuilder |
||
717 | */ |
||
718 | public function getLocation() |
||
722 | |||
723 | /** |
||
724 | * Create a field label based on the field's name. Generates title case. |
||
725 | * @param string $name |
||
726 | * @return string label |
||
727 | */ |
||
728 | protected function generateLabel($name) |
||
732 | |||
733 | /** |
||
734 | * Generates a snaked cased name. |
||
735 | * @param string $name |
||
736 | * @return string |
||
737 | */ |
||
738 | protected function generateName($name) |
||
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.