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 Form 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 Form, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
72 | class Form implements Renderable |
||
73 | { |
||
74 | /** |
||
75 | * Eloquent model of the form. |
||
76 | * |
||
77 | * @var Model |
||
78 | */ |
||
79 | protected $model; |
||
80 | |||
81 | /** |
||
82 | * @var \Illuminate\Validation\Validator |
||
83 | */ |
||
84 | protected $validator; |
||
85 | |||
86 | /** |
||
87 | * @var Builder |
||
88 | */ |
||
89 | protected $builder; |
||
90 | |||
91 | /** |
||
92 | * Submitted callback. |
||
93 | * |
||
94 | * @var Closure[] |
||
95 | */ |
||
96 | protected $submitted = []; |
||
97 | |||
98 | /** |
||
99 | * Saving callback. |
||
100 | * |
||
101 | * @var Closure[] |
||
102 | */ |
||
103 | protected $saving = []; |
||
104 | |||
105 | /** |
||
106 | * Saved callback. |
||
107 | * |
||
108 | * @var Closure[] |
||
109 | */ |
||
110 | protected $saved = []; |
||
111 | |||
112 | /** |
||
113 | * Callbacks after getting editing model. |
||
114 | * |
||
115 | * @var Closure[] |
||
116 | */ |
||
117 | protected $editing = []; |
||
118 | |||
119 | /** |
||
120 | * Data for save to current model from input. |
||
121 | * |
||
122 | * @var array |
||
123 | */ |
||
124 | protected $updates = []; |
||
125 | |||
126 | /** |
||
127 | * Data for save to model's relations from input. |
||
128 | * |
||
129 | * @var array |
||
130 | */ |
||
131 | protected $relations = []; |
||
132 | |||
133 | /** |
||
134 | * Input data. |
||
135 | * |
||
136 | * @var array |
||
137 | */ |
||
138 | protected $inputs = []; |
||
139 | |||
140 | /** |
||
141 | * Available fields. |
||
142 | * |
||
143 | * @var array |
||
144 | */ |
||
145 | public static $availableFields = []; |
||
146 | |||
147 | /** |
||
148 | * Form field alias. |
||
149 | * |
||
150 | * @var array |
||
151 | */ |
||
152 | public static $fieldAlias = []; |
||
153 | |||
154 | /** |
||
155 | * Ignored saving fields. |
||
156 | * |
||
157 | * @var array |
||
158 | */ |
||
159 | protected $ignored = []; |
||
160 | |||
161 | /** |
||
162 | * Collected field assets. |
||
163 | * |
||
164 | * @var array |
||
165 | */ |
||
166 | protected static $collectedAssets = []; |
||
167 | |||
168 | /** |
||
169 | * @var Form\Tab |
||
170 | */ |
||
171 | protected $tab = null; |
||
172 | |||
173 | /** |
||
174 | * Remove flag in `has many` form. |
||
175 | */ |
||
176 | const REMOVE_FLAG_NAME = '_remove_'; |
||
177 | |||
178 | /** |
||
179 | * Field rows in form. |
||
180 | * |
||
181 | * @var array |
||
182 | */ |
||
183 | public $rows = []; |
||
184 | |||
185 | /** |
||
186 | * @var bool |
||
187 | */ |
||
188 | protected $isSoftDeletes = false; |
||
189 | |||
190 | /** |
||
191 | * Initialization closure array. |
||
192 | * |
||
193 | * @var []Closure |
||
194 | */ |
||
195 | protected static $initCallbacks; |
||
196 | |||
197 | /** |
||
198 | * Create a new form instance. |
||
199 | * |
||
200 | * @param $model |
||
201 | * @param \Closure $callback |
||
202 | */ |
||
203 | public function __construct($model, Closure $callback = null) |
||
204 | { |
||
205 | $this->model = $model; |
||
206 | |||
207 | $this->builder = new Builder($this); |
||
208 | |||
209 | if ($callback instanceof Closure) { |
||
210 | $callback($this); |
||
211 | } |
||
212 | |||
213 | $this->isSoftDeletes = in_array(SoftDeletes::class, class_uses_deep($this->model)); |
||
214 | |||
215 | $this->callInitCallbacks(); |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * Initialize with user pre-defined default disables, etc. |
||
220 | * |
||
221 | * @param Closure $callback |
||
222 | */ |
||
223 | public static function init(Closure $callback = null) |
||
227 | |||
228 | /** |
||
229 | * Call the initialization closure array in sequence. |
||
230 | */ |
||
231 | protected function callInitCallbacks() |
||
241 | |||
242 | /** |
||
243 | * @param Field $field |
||
244 | * |
||
245 | * @return $this |
||
246 | */ |
||
247 | public function pushField(Field $field) |
||
255 | |||
256 | /** |
||
257 | * @return Model |
||
258 | */ |
||
259 | public function model() |
||
263 | |||
264 | /** |
||
265 | * @return Builder |
||
266 | */ |
||
267 | public function builder() |
||
271 | |||
272 | /** |
||
273 | * Generate a edit form. |
||
274 | * |
||
275 | * @param $id |
||
276 | * |
||
277 | * @return $this |
||
278 | */ |
||
279 | public function edit($id) |
||
288 | |||
289 | /** |
||
290 | * Use tab to split form. |
||
291 | * |
||
292 | * @param string $title |
||
293 | * @param Closure $content |
||
294 | * |
||
295 | * @return $this |
||
296 | */ |
||
297 | public function tab($title, Closure $content, $active = false) |
||
303 | |||
304 | /** |
||
305 | * Get Tab instance. |
||
306 | * |
||
307 | * @return Tab |
||
308 | */ |
||
309 | public function getTab() |
||
317 | |||
318 | /** |
||
319 | * Destroy data entity and remove files. |
||
320 | * |
||
321 | * @param $id |
||
322 | * |
||
323 | * @return mixed |
||
324 | */ |
||
325 | public function destroy($id) |
||
349 | |||
350 | /** |
||
351 | * Remove files in record. |
||
352 | * |
||
353 | * @param Model $model |
||
354 | * @param bool $forceDelete |
||
355 | */ |
||
356 | protected function deleteFiles(Model $model, $forceDelete = false) |
||
373 | |||
374 | /** |
||
375 | * Store a new record. |
||
376 | * |
||
377 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse |
||
378 | */ |
||
379 | public function store() |
||
414 | |||
415 | /** |
||
416 | * Get ajax response. |
||
417 | * |
||
418 | * @param string $message |
||
419 | * |
||
420 | * @return bool|\Illuminate\Http\JsonResponse |
||
421 | */ |
||
422 | protected function ajaxResponse($message) |
||
436 | |||
437 | /** |
||
438 | * Prepare input data for insert or update. |
||
439 | * |
||
440 | * @param array $data |
||
441 | * |
||
442 | * @return mixed |
||
443 | */ |
||
444 | protected function prepare($data = []) |
||
460 | |||
461 | /** |
||
462 | * Remove ignored fields from input. |
||
463 | * |
||
464 | * @param array $input |
||
465 | * |
||
466 | * @return array |
||
467 | */ |
||
468 | protected function removeIgnoredFields($input) |
||
474 | |||
475 | /** |
||
476 | * Get inputs for relations. |
||
477 | * |
||
478 | * @param array $inputs |
||
479 | * |
||
480 | * @return array |
||
481 | */ |
||
482 | protected function getRelationInputs($inputs = []) |
||
498 | |||
499 | /** |
||
500 | * Call editing callbacks. |
||
501 | * |
||
502 | * @return void |
||
503 | */ |
||
504 | protected function callEditing() |
||
510 | |||
511 | /** |
||
512 | * Call submitted callback. |
||
513 | * |
||
514 | * @return mixed |
||
515 | */ |
||
516 | protected function callSubmitted() |
||
524 | |||
525 | /** |
||
526 | * Call saving callback. |
||
527 | * |
||
528 | * @return mixed |
||
529 | */ |
||
530 | protected function callSaving() |
||
538 | |||
539 | /** |
||
540 | * Callback after saving a Model. |
||
541 | * |
||
542 | * @return mixed|null |
||
543 | */ |
||
544 | protected function callSaved() |
||
553 | |||
554 | /** |
||
555 | * Handle update. |
||
556 | * |
||
557 | * @param int $id |
||
558 | * @param null $data |
||
559 | * @return bool|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|null|Response |
||
560 | */ |
||
561 | public function update($id, $data = null) |
||
618 | |||
619 | /** |
||
620 | * Get RedirectResponse after store. |
||
621 | * |
||
622 | * @return \Illuminate\Http\RedirectResponse |
||
623 | */ |
||
624 | protected function redirectAfterStore() |
||
632 | |||
633 | /** |
||
634 | * Get RedirectResponse after update. |
||
635 | * |
||
636 | * @param mixed $key |
||
637 | * |
||
638 | * @return \Illuminate\Http\RedirectResponse |
||
639 | */ |
||
640 | protected function redirectAfterUpdate($key) |
||
646 | |||
647 | /** |
||
648 | * Get RedirectResponse after data saving. |
||
649 | * |
||
650 | * @param string $resourcesPath |
||
651 | * @param string $key |
||
652 | * |
||
653 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector |
||
654 | */ |
||
655 | protected function redirectAfterSaving($resourcesPath, $key) |
||
674 | |||
675 | /** |
||
676 | * Check if request is from editable. |
||
677 | * |
||
678 | * @param array $input |
||
679 | * |
||
680 | * @return bool |
||
681 | */ |
||
682 | protected function isEditable(array $input = []) |
||
686 | |||
687 | /** |
||
688 | * Handle updates for single column. |
||
689 | * |
||
690 | * @param integer $id |
||
691 | * @param array $data |
||
692 | * @return array|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|Response |
||
693 | */ |
||
694 | protected function handleColumnUpdates($id, $data) |
||
711 | |||
712 | /** |
||
713 | * Handle editable update. |
||
714 | * |
||
715 | * @param array $input |
||
716 | * |
||
717 | * @return array |
||
718 | */ |
||
719 | protected function handleEditable(array $input = []) |
||
731 | |||
732 | /** |
||
733 | * @param array $input |
||
734 | * |
||
735 | * @return array |
||
736 | */ |
||
737 | protected function handleFileDelete(array $input = []) |
||
748 | |||
749 | /** |
||
750 | * @param array $input |
||
751 | * |
||
752 | * @return array |
||
753 | */ |
||
754 | protected function handleFileSort(array $input = []) |
||
774 | |||
775 | /** |
||
776 | * Handle orderable update. |
||
777 | * |
||
778 | * @param int $id |
||
779 | * @param array $input |
||
780 | * |
||
781 | * @return bool |
||
782 | */ |
||
783 | protected function handleOrderable($id, array $input = []) |
||
797 | |||
798 | /** |
||
799 | * Update relation data. |
||
800 | * |
||
801 | * @param array $relationsData |
||
802 | * |
||
803 | * @return void |
||
804 | */ |
||
805 | protected function updateRelation($relationsData) |
||
912 | |||
913 | /** |
||
914 | * Prepare input data for update. |
||
915 | * |
||
916 | * @param array $updates |
||
917 | * @param bool $oneToOneRelation If column is one-to-one relation. |
||
918 | * |
||
919 | * @return array |
||
920 | */ |
||
921 | protected function prepareUpdate(array $updates, $oneToOneRelation = false) |
||
953 | |||
954 | /** |
||
955 | * @param string|array $columns |
||
956 | * @param bool $oneToOneRelation |
||
957 | * |
||
958 | * @return bool |
||
959 | */ |
||
960 | protected function invalidColumn($columns, $oneToOneRelation = false) |
||
971 | |||
972 | /** |
||
973 | * Prepare input data for insert. |
||
974 | * |
||
975 | * @param $inserts |
||
976 | * |
||
977 | * @return array |
||
978 | */ |
||
979 | protected function prepareInsert($inserts) |
||
1002 | |||
1003 | /** |
||
1004 | * Is input data is has-one relation. |
||
1005 | * |
||
1006 | * @param array $inserts |
||
1007 | * |
||
1008 | * @return bool |
||
1009 | */ |
||
1010 | protected function isHasOneRelation($inserts) |
||
1024 | |||
1025 | /** |
||
1026 | * Set after getting editing model callback. |
||
1027 | * |
||
1028 | * @param Closure $callback |
||
1029 | * |
||
1030 | * @return void |
||
1031 | */ |
||
1032 | public function editing(Closure $callback) |
||
1036 | |||
1037 | /** |
||
1038 | * Set submitted callback. |
||
1039 | * |
||
1040 | * @param Closure $callback |
||
1041 | * |
||
1042 | * @return void |
||
1043 | */ |
||
1044 | public function submitted(Closure $callback) |
||
1048 | |||
1049 | /** |
||
1050 | * Set saving callback. |
||
1051 | * |
||
1052 | * @param Closure $callback |
||
1053 | * |
||
1054 | * @return void |
||
1055 | */ |
||
1056 | public function saving(Closure $callback) |
||
1060 | |||
1061 | /** |
||
1062 | * Set saved callback. |
||
1063 | * |
||
1064 | * @param Closure $callback |
||
1065 | * |
||
1066 | * @return void |
||
1067 | */ |
||
1068 | public function saved(Closure $callback) |
||
1072 | |||
1073 | /** |
||
1074 | * Ignore fields to save. |
||
1075 | * |
||
1076 | * @param string|array $fields |
||
1077 | * |
||
1078 | * @return $this |
||
1079 | */ |
||
1080 | public function ignore($fields) |
||
1086 | |||
1087 | /** |
||
1088 | * @param array $data |
||
1089 | * @param string|array $columns |
||
1090 | * |
||
1091 | * @return array|mixed |
||
1092 | */ |
||
1093 | View Code Duplication | protected function getDataByColumn($data, $columns) |
|
1111 | |||
1112 | /** |
||
1113 | * Find field object by column. |
||
1114 | * |
||
1115 | * @param $column |
||
1116 | * |
||
1117 | * @return mixed |
||
1118 | */ |
||
1119 | protected function getFieldByColumn($column) |
||
1131 | |||
1132 | /** |
||
1133 | * Set original data for each field. |
||
1134 | * |
||
1135 | * @return void |
||
1136 | */ |
||
1137 | protected function setFieldOriginalValue() |
||
1147 | |||
1148 | /** |
||
1149 | * Set all fields value in form. |
||
1150 | * |
||
1151 | * @param $id |
||
1152 | * |
||
1153 | * @return void |
||
1154 | */ |
||
1155 | protected function setFieldValue($id) |
||
1179 | |||
1180 | /** |
||
1181 | * Don't snake case attributes. |
||
1182 | * |
||
1183 | * @param Model $model |
||
1184 | * |
||
1185 | * @return void |
||
1186 | */ |
||
1187 | protected static function doNotSnakeAttributes(Model $model) |
||
1193 | |||
1194 | /** |
||
1195 | * Get validation messages. |
||
1196 | * |
||
1197 | * @param array $input |
||
1198 | * |
||
1199 | * @return MessageBag|bool |
||
1200 | */ |
||
1201 | public function validationMessages($input) |
||
1220 | |||
1221 | /** |
||
1222 | * Merge validation messages from input validators. |
||
1223 | * |
||
1224 | * @param \Illuminate\Validation\Validator[] $validators |
||
1225 | * |
||
1226 | * @return MessageBag |
||
1227 | */ |
||
1228 | protected function mergeValidationMessages($validators) |
||
1238 | |||
1239 | /** |
||
1240 | * Get all relations of model from callable. |
||
1241 | * |
||
1242 | * @return array |
||
1243 | */ |
||
1244 | public function getRelations() |
||
1271 | |||
1272 | /** |
||
1273 | * Set action for form. |
||
1274 | * |
||
1275 | * @param string $action |
||
1276 | * |
||
1277 | * @return $this |
||
1278 | */ |
||
1279 | public function setAction($action) |
||
1285 | |||
1286 | /** |
||
1287 | * Set field and label width in current form. |
||
1288 | * |
||
1289 | * @param int $fieldWidth |
||
1290 | * @param int $labelWidth |
||
1291 | * |
||
1292 | * @return $this |
||
1293 | */ |
||
1294 | public function setWidth($fieldWidth = 8, $labelWidth = 2) |
||
1305 | |||
1306 | /** |
||
1307 | * Set view for form. |
||
1308 | * |
||
1309 | * @param string $view |
||
1310 | * |
||
1311 | * @return $this |
||
1312 | */ |
||
1313 | public function setView($view) |
||
1319 | |||
1320 | /** |
||
1321 | * Set title for form. |
||
1322 | * |
||
1323 | * @param string $title |
||
1324 | * |
||
1325 | * @return $this |
||
1326 | */ |
||
1327 | public function setTitle($title = '') |
||
1333 | |||
1334 | /** |
||
1335 | * Add a row in form. |
||
1336 | * |
||
1337 | * @param Closure $callback |
||
1338 | * |
||
1339 | * @return $this |
||
1340 | */ |
||
1341 | public function row(Closure $callback) |
||
1347 | |||
1348 | /** |
||
1349 | * Tools setting for form. |
||
1350 | * |
||
1351 | * @param Closure $callback |
||
1352 | */ |
||
1353 | public function tools(Closure $callback) |
||
1357 | |||
1358 | /** |
||
1359 | * Disable form submit. |
||
1360 | * |
||
1361 | * @return $this |
||
1362 | * |
||
1363 | * @deprecated |
||
1364 | */ |
||
1365 | public function disableSubmit(bool $disable = true) |
||
1371 | |||
1372 | /** |
||
1373 | * Disable form reset. |
||
1374 | * |
||
1375 | * @return $this |
||
1376 | * |
||
1377 | * @deprecated |
||
1378 | */ |
||
1379 | public function disableReset(bool $disable = true) |
||
1385 | |||
1386 | /** |
||
1387 | * Disable View Checkbox on footer. |
||
1388 | * |
||
1389 | * @return $this |
||
1390 | */ |
||
1391 | public function disableViewCheck(bool $disable = true) |
||
1397 | |||
1398 | /** |
||
1399 | * Disable Editing Checkbox on footer. |
||
1400 | * |
||
1401 | * @return $this |
||
1402 | */ |
||
1403 | public function disableEditingCheck(bool $disable = true) |
||
1409 | |||
1410 | /** |
||
1411 | * Disable Creating Checkbox on footer. |
||
1412 | * |
||
1413 | * @return $this |
||
1414 | */ |
||
1415 | public function disableCreatingCheck(bool $disable = true) |
||
1421 | |||
1422 | /** |
||
1423 | * Footer setting for form. |
||
1424 | * |
||
1425 | * @param Closure $callback |
||
1426 | */ |
||
1427 | public function footer(Closure $callback) |
||
1431 | |||
1432 | /** |
||
1433 | * Get current resource route url. |
||
1434 | * |
||
1435 | * @param int $slice |
||
1436 | * |
||
1437 | * @return string |
||
1438 | */ |
||
1439 | public function resource($slice = -2) |
||
1449 | |||
1450 | /** |
||
1451 | * Render the form contents. |
||
1452 | * |
||
1453 | * @return string |
||
1454 | */ |
||
1455 | public function render() |
||
1463 | |||
1464 | /** |
||
1465 | * Get or set input data. |
||
1466 | * |
||
1467 | * @param string $key |
||
1468 | * @param null $value |
||
1469 | * |
||
1470 | * @return array|mixed |
||
1471 | */ |
||
1472 | public function input($key, $value = null) |
||
1480 | |||
1481 | /** |
||
1482 | * Register builtin fields. |
||
1483 | * |
||
1484 | * @return void |
||
1485 | */ |
||
1486 | public static function registerBuiltinFields() |
||
1541 | |||
1542 | /** |
||
1543 | * Register custom field. |
||
1544 | * |
||
1545 | * @param string $abstract |
||
1546 | * @param string $class |
||
1547 | * |
||
1548 | * @return void |
||
1549 | */ |
||
1550 | public static function extend($abstract, $class) |
||
1554 | |||
1555 | /** |
||
1556 | * Set form field alias. |
||
1557 | * |
||
1558 | * @param string $field |
||
1559 | * @param string $alias |
||
1560 | * |
||
1561 | * @return void |
||
1562 | */ |
||
1563 | public static function alias($field, $alias) |
||
1567 | |||
1568 | /** |
||
1569 | * Remove registered field. |
||
1570 | * |
||
1571 | * @param array|string $abstract |
||
1572 | */ |
||
1573 | public static function forget($abstract) |
||
1577 | |||
1578 | /** |
||
1579 | * Find field class. |
||
1580 | * |
||
1581 | * @param string $method |
||
1582 | * |
||
1583 | * @return bool|mixed |
||
1584 | */ |
||
1585 | public static function findFieldClass($method) |
||
1600 | |||
1601 | /** |
||
1602 | * Collect assets required by registered field. |
||
1603 | * |
||
1604 | * @return array |
||
1605 | */ |
||
1606 | public static function collectFieldAssets() |
||
1631 | |||
1632 | /** |
||
1633 | * Getter. |
||
1634 | * |
||
1635 | * @param string $name |
||
1636 | * |
||
1637 | * @return array|mixed |
||
1638 | */ |
||
1639 | public function __get($name) |
||
1643 | |||
1644 | /** |
||
1645 | * Setter. |
||
1646 | * |
||
1647 | * @param string $name |
||
1648 | * @param $value |
||
1649 | */ |
||
1650 | public function __set($name, $value) |
||
1654 | |||
1655 | /** |
||
1656 | * Generate a Field object and add to form builder if Field exists. |
||
1657 | * |
||
1658 | * @param string $method |
||
1659 | * @param array $arguments |
||
1660 | * |
||
1661 | * @return Field |
||
1662 | */ |
||
1663 | public function __call($method, $arguments) |
||
1679 | } |
||
1680 |
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.