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 MenuItem 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 MenuItem, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class MenuItem implements ArrayableContract |
||
24 | { |
||
25 | /** |
||
26 | * Array properties. |
||
27 | * |
||
28 | * @var array |
||
29 | */ |
||
30 | protected $properties = []; |
||
31 | |||
32 | /** |
||
33 | * The child collections for current menu item. |
||
34 | * |
||
35 | * @var array |
||
36 | */ |
||
37 | protected $childs = array(); |
||
38 | |||
39 | /** |
||
40 | * The fillable attribute. |
||
41 | * |
||
42 | * @var array |
||
43 | */ |
||
44 | protected $fillable = array( |
||
45 | 'url', |
||
46 | 'route', |
||
47 | 'title', |
||
48 | 'name', |
||
49 | 'icon', |
||
50 | 'parent', |
||
51 | 'attributes', |
||
52 | 'active', |
||
53 | 'order', |
||
54 | 'hideWhen', |
||
55 | ); |
||
56 | |||
57 | /** |
||
58 | * The hideWhen callback. |
||
59 | * |
||
60 | * @var Closure |
||
61 | */ |
||
62 | protected $hideWhen; |
||
63 | |||
64 | /** |
||
65 | * Constructor. |
||
66 | * |
||
67 | * @param array $properties |
||
68 | */ |
||
69 | 24 | public function __construct($properties = array()) |
|
70 | { |
||
71 | 24 | $this->properties = $properties; |
|
72 | 24 | $this->fill($properties); |
|
73 | 24 | } |
|
74 | |||
75 | /** |
||
76 | * Set the icon property when the icon is defined in the link attributes. |
||
77 | * |
||
78 | * @param array $properties |
||
79 | * |
||
80 | * @return array |
||
81 | */ |
||
82 | 24 | protected static function setIconAttribute(array $properties) |
|
83 | { |
||
84 | 24 | $icon = Arr::get($properties, 'attributes.icon'); |
|
85 | 24 | if (!is_null($icon)) { |
|
86 | 1 | $properties['icon'] = $icon; |
|
87 | |||
88 | 1 | Arr::forget($properties, 'attributes.icon'); |
|
89 | |||
90 | 1 | return $properties; |
|
91 | } |
||
92 | |||
93 | 23 | return $properties; |
|
94 | } |
||
95 | |||
96 | /** |
||
97 | * Get random name. |
||
98 | * |
||
99 | * @param array $attributes |
||
100 | * |
||
101 | * @return string |
||
102 | */ |
||
103 | protected static function getRandomName(array $attributes) |
||
104 | { |
||
105 | return substr(md5(Arr::get($attributes, 'title', Str::random(6))), 0, 5); |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * Create new static instance. |
||
110 | * |
||
111 | * @param array $properties |
||
112 | * |
||
113 | * @return static |
||
114 | */ |
||
115 | 24 | public static function make(array $properties) |
|
116 | { |
||
117 | 24 | $properties = self::setIconAttribute($properties); |
|
118 | |||
119 | 24 | return new static($properties); |
|
120 | } |
||
121 | |||
122 | /** |
||
123 | * Fill the attributes. |
||
124 | * |
||
125 | * @param array $attributes |
||
126 | */ |
||
127 | 24 | public function fill($attributes) |
|
128 | { |
||
129 | 24 | foreach ($attributes as $key => $value) { |
|
130 | 23 | if (in_array($key, $this->fillable)) { |
|
131 | 23 | $this->{$key} = $value; |
|
132 | } |
||
133 | } |
||
134 | 24 | } |
|
135 | |||
136 | /** |
||
137 | * Create new menu child item using array. |
||
138 | * |
||
139 | * @param $attributes |
||
140 | * |
||
141 | * @return $this |
||
142 | */ |
||
143 | 2 | public function child($attributes) |
|
149 | |||
150 | /** |
||
151 | * Register new child menu with dropdown. |
||
152 | * |
||
153 | * @param $title |
||
154 | * @param callable $callback |
||
155 | * |
||
156 | * @return $this |
||
157 | */ |
||
158 | 9 | View Code Duplication | public function dropdown($title, \Closure $callback, $order = 0, array $attributes = array()) |
179 | |||
180 | /** |
||
181 | * Create new menu item and set the action to route. |
||
182 | * |
||
183 | * @param $route |
||
184 | * @param $title |
||
185 | * @param array $parameters |
||
186 | * @param array $attributes |
||
187 | * |
||
188 | * @return MenuItem |
||
189 | */ |
||
190 | 3 | public function route($route, $title, $parameters = array(), $order = 0, $attributes = array()) |
|
206 | |||
207 | /** |
||
208 | * Create new menu item and set the action to url. |
||
209 | * |
||
210 | * @param $url |
||
211 | * @param $title |
||
212 | * @param array $attributes |
||
213 | * |
||
214 | * @return MenuItem |
||
215 | */ |
||
216 | 6 | public function url($url, $title, $order = 0, $attributes = array()) |
|
230 | |||
231 | /** |
||
232 | * Add new child item. |
||
233 | * |
||
234 | * @param array $properties |
||
235 | * |
||
236 | * @return $this |
||
237 | */ |
||
238 | 9 | public function add(array $properties) |
|
246 | |||
247 | /** |
||
248 | * Add new divider. |
||
249 | * |
||
250 | * @param int $order |
||
251 | * |
||
252 | * @return self |
||
253 | */ |
||
254 | 1 | public function addDivider($order = null) |
|
262 | |||
263 | /** |
||
264 | * Alias method instead "addDivider". |
||
265 | * |
||
266 | * @param int $order |
||
267 | * |
||
268 | * @return MenuItem |
||
269 | */ |
||
270 | 1 | public function divider($order = null) |
|
274 | |||
275 | /** |
||
276 | * Add dropdown header. |
||
277 | * |
||
278 | * @param $title |
||
279 | * |
||
280 | * @return $this |
||
281 | */ |
||
282 | 2 | public function addHeader($title) |
|
293 | |||
294 | /** |
||
295 | * Same with "addHeader" method. |
||
296 | * |
||
297 | * @param $title |
||
298 | * |
||
299 | * @return $this |
||
300 | */ |
||
301 | 2 | public function header($title) |
|
305 | |||
306 | /** |
||
307 | * Get childs. |
||
308 | * |
||
309 | * @return array |
||
310 | */ |
||
311 | 10 | public function getChilds() |
|
319 | |||
320 | /** |
||
321 | * Get url. |
||
322 | * |
||
323 | * @return string |
||
324 | */ |
||
325 | 3 | public function getUrl() |
|
337 | |||
338 | /** |
||
339 | * Get request url. |
||
340 | * |
||
341 | * @return string |
||
342 | */ |
||
343 | 1 | public function getRequest() |
|
347 | |||
348 | /** |
||
349 | * Get icon. |
||
350 | * |
||
351 | * @param null|string $default |
||
352 | * |
||
353 | * @return string |
||
354 | */ |
||
355 | 3 | public function getIcon($default = null) |
|
366 | |||
367 | /** |
||
368 | * Get properties. |
||
369 | * |
||
370 | * @return array |
||
371 | */ |
||
372 | 2 | public function getProperties() |
|
376 | |||
377 | /** |
||
378 | * Get HTML attribute data. |
||
379 | * |
||
380 | * @return mixed |
||
381 | */ |
||
382 | 1 | public function getAttributes() |
|
390 | |||
391 | /** |
||
392 | * Check is the current item divider. |
||
393 | * |
||
394 | * @return bool |
||
395 | */ |
||
396 | 1 | public function isDivider() |
|
400 | |||
401 | /** |
||
402 | * Check is the current item divider. |
||
403 | * |
||
404 | * @return bool |
||
405 | */ |
||
406 | 1 | public function isHeader() |
|
410 | |||
411 | /** |
||
412 | * Check is the current item divider. |
||
413 | * |
||
414 | * @param $name |
||
415 | * |
||
416 | * @return bool |
||
417 | */ |
||
418 | 2 | public function is($name) |
|
422 | |||
423 | /** |
||
424 | * Check is the current item has sub menu . |
||
425 | * |
||
426 | * @return bool |
||
427 | */ |
||
428 | 1 | public function hasSubMenu() |
|
432 | |||
433 | /** |
||
434 | * Same with hasSubMenu. |
||
435 | * |
||
436 | * @return bool |
||
437 | */ |
||
438 | 1 | public function hasChilds() |
|
442 | |||
443 | /** |
||
444 | * Check the active state for current menu. |
||
445 | * |
||
446 | * @return mixed |
||
447 | */ |
||
448 | public function hasActiveOnChild() |
||
456 | |||
457 | /** |
||
458 | * Get active state from child menu items. |
||
459 | * |
||
460 | * @return bool |
||
461 | */ |
||
462 | public function getActiveStateFromChilds() |
||
483 | |||
484 | /** |
||
485 | * Get inactive state. |
||
486 | * |
||
487 | * @return bool |
||
488 | */ |
||
489 | public function inactive() |
||
503 | |||
504 | /** |
||
505 | * Get active attribute. |
||
506 | * |
||
507 | * @return string |
||
508 | */ |
||
509 | public function getActiveAttribute() |
||
513 | |||
514 | /** |
||
515 | * Get inactive attribute. |
||
516 | * |
||
517 | * @return string |
||
518 | */ |
||
519 | public function getInactiveAttribute() |
||
523 | |||
524 | /** |
||
525 | * Get active state for current item. |
||
526 | * |
||
527 | * @return mixed |
||
528 | */ |
||
529 | public function isActive() |
||
551 | |||
552 | /** |
||
553 | * Determine the current item using route. |
||
554 | * |
||
555 | * @return bool |
||
556 | */ |
||
557 | protected function hasRoute() |
||
561 | |||
562 | /** |
||
563 | * Get active status using route. |
||
564 | * |
||
565 | * @return bool |
||
566 | */ |
||
567 | protected function getActiveStateFromRoute() |
||
571 | |||
572 | /** |
||
573 | * Get active status using request url. |
||
574 | * |
||
575 | * @return bool |
||
576 | */ |
||
577 | protected function getActiveStateFromUrl() |
||
581 | |||
582 | /** |
||
583 | * Set order value. |
||
584 | * |
||
585 | * @param int $order |
||
586 | * @return self |
||
587 | */ |
||
588 | public function order($order) |
||
594 | |||
595 | /** |
||
596 | * Set hide condition for current menu item. |
||
597 | * |
||
598 | * @param Closure |
||
599 | * @return boolean |
||
600 | */ |
||
601 | public function hideWhen(Closure $callback) |
||
607 | |||
608 | /** |
||
609 | * Determine whether the menu item is hidden. |
||
610 | * |
||
611 | * @return boolean |
||
612 | */ |
||
613 | public function hidden() |
||
621 | |||
622 | /** |
||
623 | * Get the instance as an array. |
||
624 | * |
||
625 | * @return array |
||
626 | */ |
||
627 | public function toArray() |
||
631 | |||
632 | /** |
||
633 | * Get property. |
||
634 | * |
||
635 | * @param string $key |
||
636 | * |
||
637 | * @return string|null |
||
638 | */ |
||
639 | 4 | public function __get($key) |
|
643 | } |
||
644 |
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.