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 TreeDropdownField 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 TreeDropdownField, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
57 | class TreeDropdownField extends FormField |
||
58 | { |
||
59 | protected $schemaDataType = self::SCHEMA_DATA_TYPE_SINGLESELECT; |
||
60 | |||
61 | protected $schemaComponent = 'TreeDropdownField'; |
||
62 | |||
63 | private static $url_handlers = array( |
||
|
|||
64 | '$Action!/$ID' => '$Action' |
||
65 | ); |
||
66 | |||
67 | private static $allowed_actions = array( |
||
68 | 'tree' |
||
69 | ); |
||
70 | |||
71 | /** |
||
72 | * @var string |
||
73 | */ |
||
74 | protected $emptyString = null; |
||
75 | |||
76 | /** |
||
77 | * @var bool |
||
78 | */ |
||
79 | protected $hasEmptyDefault = false; |
||
80 | |||
81 | /** |
||
82 | * Class name for underlying object |
||
83 | * |
||
84 | * @var string |
||
85 | */ |
||
86 | protected $sourceObject = null; |
||
87 | |||
88 | /** |
||
89 | * Name of key field on underlying object |
||
90 | * |
||
91 | * @var string |
||
92 | */ |
||
93 | protected $keyField = null; |
||
94 | |||
95 | /** |
||
96 | * Name of lavel field on underlying object |
||
97 | * |
||
98 | * @var string |
||
99 | */ |
||
100 | protected $labelField = null; |
||
101 | |||
102 | /** |
||
103 | * Callback for filtering records |
||
104 | * |
||
105 | * @var callable |
||
106 | */ |
||
107 | protected $filterCallback = null; |
||
108 | |||
109 | /** |
||
110 | * Callback for marking record as disabled |
||
111 | * |
||
112 | * @var callable |
||
113 | */ |
||
114 | protected $disableCallback = null; |
||
115 | |||
116 | /** |
||
117 | * Callback for searching records. This callback takes the following arguments: |
||
118 | * - sourceObject Object class to search |
||
119 | * - labelField Label field |
||
120 | * - search Search text |
||
121 | * |
||
122 | * @var callable |
||
123 | */ |
||
124 | protected $searchCallback = null; |
||
125 | |||
126 | /** |
||
127 | * Filter for base record |
||
128 | * |
||
129 | * @var int |
||
130 | */ |
||
131 | protected $baseID = 0; |
||
132 | |||
133 | /** |
||
134 | * Default child method in Hierarchy->getChildrenAsUL |
||
135 | * |
||
136 | * @var string |
||
137 | */ |
||
138 | protected $childrenMethod = 'AllChildrenIncludingDeleted'; |
||
139 | |||
140 | /** |
||
141 | * Default child counting method in Hierarchy->getChildrenAsUL |
||
142 | * |
||
143 | * @var string |
||
144 | */ |
||
145 | protected $numChildrenMethod = 'numChildren'; |
||
146 | |||
147 | /** |
||
148 | * Current string value for search text to filter on |
||
149 | * |
||
150 | * @var string |
||
151 | */ |
||
152 | protected $search = null; |
||
153 | |||
154 | /** |
||
155 | * List of ids in current search result (keys are ids, values are true) |
||
156 | * |
||
157 | * @var array |
||
158 | */ |
||
159 | protected $searchIds = []; |
||
160 | |||
161 | /** |
||
162 | * Determine if search should be shown |
||
163 | * |
||
164 | * @var bool |
||
165 | */ |
||
166 | protected $showSearch = false; |
||
167 | |||
168 | /** |
||
169 | * List of ids which have their search expanded (keys are ids, values are true) |
||
170 | * |
||
171 | * @var array |
||
172 | */ |
||
173 | protected $searchExpanded = []; |
||
174 | |||
175 | /** |
||
176 | * CAVEAT: for search to work properly $labelField must be a database field, |
||
177 | * or you need to setSearchFunction. |
||
178 | * |
||
179 | * @param string $name the field name |
||
180 | * @param string $title the field label |
||
181 | * @param string $sourceObject A DataObject class name with the {@link Hierarchy} extension. |
||
182 | * @param string $keyField to field on the source class to save as the |
||
183 | * field value (default ID). |
||
184 | * @param string $labelField the field name to show as the human-readable |
||
185 | * value on the tree (default Title). |
||
186 | * @param bool $showSearch enable the ability to search the tree by |
||
187 | * entering the text in the input field. |
||
188 | */ |
||
189 | public function __construct( |
||
218 | |||
219 | /** |
||
220 | * Set the ID of the root node of the tree. This defaults to 0 - i.e. |
||
221 | * displays the whole tree. |
||
222 | * |
||
223 | * @param int $ID |
||
224 | * @return $this |
||
225 | */ |
||
226 | public function setTreeBaseID($ID) |
||
231 | |||
232 | /** |
||
233 | * Set a callback used to filter the values of the tree before |
||
234 | * displaying to the user. |
||
235 | * |
||
236 | * @param callback $callback |
||
237 | * @return $this |
||
238 | */ |
||
239 | public function setFilterFunction($callback) |
||
248 | |||
249 | /** |
||
250 | * Set a callback used to disable checkboxes for some items in the tree |
||
251 | * |
||
252 | * @param callback $callback |
||
253 | * @return $this |
||
254 | */ |
||
255 | public function setDisableFunction($callback) |
||
264 | |||
265 | /** |
||
266 | * Set a callback used to search the hierarchy globally, even before |
||
267 | * applying the filter. |
||
268 | * |
||
269 | * @param callback $callback |
||
270 | * @return $this |
||
271 | */ |
||
272 | public function setSearchFunction($callback) |
||
281 | |||
282 | /** |
||
283 | * Check if search is shown |
||
284 | * |
||
285 | * @return bool |
||
286 | */ |
||
287 | public function getShowSearch() |
||
291 | |||
292 | /** |
||
293 | * @param bool $bool |
||
294 | * @return $this |
||
295 | */ |
||
296 | public function setShowSearch($bool) |
||
301 | |||
302 | /** |
||
303 | * @param string $method The parameter to ChildrenMethod to use when calling Hierarchy->getChildrenAsUL in |
||
304 | * {@link Hierarchy}. The method specified determines the structure of the returned list. Use "ChildFolders" |
||
305 | * in place of the default to get a drop-down listing with only folders, i.e. not including the child elements in |
||
306 | * the currently selected folder. setNumChildrenMethod() should be used as well for proper functioning. |
||
307 | * |
||
308 | * See {@link Hierarchy} for a complete list of possible methods. |
||
309 | * @return $this |
||
310 | */ |
||
311 | public function setChildrenMethod($method) |
||
316 | |||
317 | /** |
||
318 | * @param string $method The parameter to numChildrenMethod to use when calling Hierarchy->getChildrenAsUL in |
||
319 | * {@link Hierarchy}. Should be used in conjunction with setChildrenMethod(). |
||
320 | * |
||
321 | * @return $this |
||
322 | */ |
||
323 | public function setNumChildrenMethod($method) |
||
328 | |||
329 | /** |
||
330 | * @param array $properties |
||
331 | * @return string |
||
332 | */ |
||
333 | public function Field($properties = array()) |
||
361 | |||
362 | public function extraClass() |
||
366 | |||
367 | /** |
||
368 | * Get the whole tree of a part of the tree via an AJAX request. |
||
369 | * |
||
370 | * @param HTTPRequest $request |
||
371 | * @return HTTPResponse |
||
372 | * @throws Exception |
||
373 | */ |
||
374 | public function tree(HTTPRequest $request) |
||
471 | |||
472 | /** |
||
473 | * Marking public function for the tree, which combines different filters sensibly. |
||
474 | * If a filter function has been set, that will be called. And if search text is set, |
||
475 | * filter on that too. Return true if all applicable conditions are true, false otherwise. |
||
476 | * |
||
477 | * @param DataObject $node |
||
478 | * @return bool |
||
479 | */ |
||
480 | public function filterMarking($node) |
||
492 | |||
493 | /** |
||
494 | * Marking a specific node in the tree as disabled |
||
495 | * @param $node |
||
496 | * @return boolean |
||
497 | */ |
||
498 | public function nodeIsDisabled($node) |
||
502 | |||
503 | /** |
||
504 | * @param string $field |
||
505 | * @return $this |
||
506 | */ |
||
507 | public function setLabelField($field) |
||
512 | |||
513 | /** |
||
514 | * @return String |
||
515 | */ |
||
516 | public function getLabelField() |
||
520 | |||
521 | /** |
||
522 | * @param string $field |
||
523 | * @return $this |
||
524 | */ |
||
525 | public function setKeyField($field) |
||
530 | |||
531 | /** |
||
532 | * @return String |
||
533 | */ |
||
534 | public function getKeyField() |
||
538 | |||
539 | /** |
||
540 | * @param string $class |
||
541 | * @return $this |
||
542 | */ |
||
543 | public function setSourceObject($class) |
||
548 | |||
549 | /** |
||
550 | * @return String |
||
551 | */ |
||
552 | public function getSourceObject() |
||
556 | |||
557 | /** |
||
558 | * Populate $this->searchIds with the IDs of the pages matching the searched parameter and their parents. |
||
559 | * Reverse-constructs the tree starting from the leaves. Initially taken from CMSSiteTreeFilter, but modified |
||
560 | * with pluggable search function. |
||
561 | */ |
||
562 | protected function populateIDs() |
||
617 | |||
618 | /** |
||
619 | * Get the object where the $keyField is equal to a certain value |
||
620 | * |
||
621 | * @param string|int $key |
||
622 | * @return DataObject |
||
623 | */ |
||
624 | protected function objectForKey($key) |
||
630 | |||
631 | /** |
||
632 | * Changes this field to the readonly field. |
||
633 | */ |
||
634 | View Code Duplication | public function performReadonlyTransformation() |
|
643 | |||
644 | /** |
||
645 | * @param string|FormField $classOrCopy |
||
646 | * @return FormField |
||
647 | */ |
||
648 | public function castedCopy($classOrCopy) |
||
658 | |||
659 | public function getSchemaStateDefaults() |
||
678 | |||
679 | public function getSchemaDataDefaults() |
||
688 | |||
689 | /** |
||
690 | * @param boolean $bool |
||
691 | * @return self Self reference |
||
692 | */ |
||
693 | public function setHasEmptyDefault($bool) |
||
698 | |||
699 | /** |
||
700 | * @return bool |
||
701 | */ |
||
702 | public function getHasEmptyDefault() |
||
706 | |||
707 | /** |
||
708 | * Set the default selection label, e.g. "select...". |
||
709 | * Defaults to an empty string. Automatically sets |
||
710 | * {@link $hasEmptyDefault} to true. |
||
711 | * |
||
712 | * @param string $string |
||
713 | * @return $this |
||
714 | */ |
||
715 | public function setEmptyString($string) |
||
721 | |||
722 | /** |
||
723 | * @return string |
||
724 | */ |
||
725 | public function getEmptyString() |
||
739 | } |
||
740 |