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 Metable 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 Metable, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | trait Metable |
||
14 | { |
||
15 | /** |
||
16 | * Query methods customizable by this trait. |
||
17 | * |
||
18 | * @var array |
||
19 | */ |
||
20 | protected $metaQueryable = [ |
||
21 | 'where', 'whereBetween', 'whereIn', 'whereNull', |
||
22 | 'whereDate', 'whereYear', 'whereMonth', 'whereDay', |
||
23 | 'orderBy', 'pluck', 'value', 'aggregate', 'lists' |
||
24 | ]; |
||
25 | |||
26 | /** |
||
27 | * Register hooks for the trait. |
||
28 | * |
||
29 | * @codeCoverageIgnore |
||
30 | * |
||
31 | * @return void |
||
32 | */ |
||
33 | View Code Duplication | public static function bootMetable() |
|
50 | |||
51 | /** |
||
52 | * Determine wheter method called on the query is customizable by this trait. |
||
53 | * |
||
54 | * @param string $method |
||
55 | * @return boolean |
||
56 | */ |
||
57 | protected function isMetaQueryable($method) |
||
61 | |||
62 | /** |
||
63 | * Custom query handler for querying meta attributes. |
||
64 | * |
||
65 | * @param \Sofa\Eloquence\Builder $query |
||
66 | * @param string $method |
||
67 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
68 | * @return mixed |
||
69 | */ |
||
70 | protected function metaQuery(Builder $query, $method, ArgumentBag $args) |
||
78 | |||
79 | /** |
||
80 | * Adjust meta columns for select statement. |
||
81 | * |
||
82 | * @param \Sofa\Eloquence\Builder $query |
||
83 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
84 | * @return void |
||
85 | */ |
||
86 | protected function metaSelect(Builder $query, ArgumentBag $args) |
||
110 | |||
111 | /** |
||
112 | * Join meta attributes table in order to call provided method. |
||
113 | * |
||
114 | * @param \Sofa\Eloquence\Builder $query |
||
115 | * @param string $method |
||
116 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
117 | * @return mixed |
||
118 | */ |
||
119 | protected function metaJoinQuery(Builder $query, $method, ArgumentBag $args) |
||
131 | |||
132 | /** |
||
133 | * Order query by meta attribute. |
||
134 | * |
||
135 | * @param \Sofa\Eloquence\Builder $query |
||
136 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
137 | * @param string $alias |
||
138 | * @return \Sofa\Eloquence\Builder |
||
139 | */ |
||
140 | protected function orderByMeta(Builder $query, $args, $alias) |
||
146 | |||
147 | protected function listsMeta(Builder $query, ArgumentBag $args, $alias) |
||
151 | |||
152 | /** |
||
153 | * Get an array with the values of given meta attribute. |
||
154 | * |
||
155 | * @param \Sofa\Eloquence\Builder $query |
||
156 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
157 | * @param string $alias |
||
158 | * @return array |
||
159 | */ |
||
160 | protected function pluckMeta(Builder $query, ArgumentBag $args, $alias) |
||
172 | |||
173 | /** |
||
174 | * Add select clause for key of the list array. |
||
175 | * |
||
176 | * @param \Sofa\Eloquence\Builder $query |
||
177 | * @param string $key |
||
178 | * @return \Sofa\Eloquence\Builder |
||
179 | */ |
||
180 | protected function metaSelectListsKey(Builder $query, $key) |
||
192 | |||
193 | /** |
||
194 | * Get single value result from the meta attribute. |
||
195 | * |
||
196 | * @param \Sofa\Eloquence\Builder $query |
||
197 | * @param string $method |
||
198 | * @param string $alias |
||
199 | * @return mixed |
||
200 | */ |
||
201 | protected function metaSingleResult(Builder $query, $method, $alias) |
||
205 | |||
206 | |||
207 | /** |
||
208 | * Join meta attributes table. |
||
209 | * |
||
210 | * @param \Sofa\Eloquence\Builder $query |
||
211 | * @param string $column |
||
212 | * @return string |
||
213 | */ |
||
214 | protected function joinMeta(Builder $query, $column) |
||
230 | |||
231 | /** |
||
232 | * Generate unique alias for meta attributes table. |
||
233 | * |
||
234 | * @return string |
||
235 | */ |
||
236 | protected function generateMetaAlias() |
||
240 | |||
241 | /** |
||
242 | * Add whereHas subquery on the meta attributes relation. |
||
243 | * |
||
244 | * @param \Sofa\Eloquence\Builder $query |
||
245 | * @param string $method |
||
246 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
247 | * @return \Sofa\Eloquence\Builder |
||
248 | */ |
||
249 | protected function metaHasQuery(Builder $query, $method, ArgumentBag $args) |
||
263 | |||
264 | /** |
||
265 | * Get boolean called on the original method and set it to default. |
||
266 | * |
||
267 | * @param \Sofa\EloquenceArgumentBag $args |
||
268 | * @return string |
||
269 | */ |
||
270 | protected function getMetaBoolean(ArgumentBag $args) |
||
278 | |||
279 | /** |
||
280 | * Determine the operator for count relation query. |
||
281 | * |
||
282 | * @param string $method |
||
283 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
284 | * @return string |
||
285 | */ |
||
286 | protected function getMetaOperator($method, ArgumentBag $args) |
||
294 | |||
295 | /** |
||
296 | * Integers and floats must be passed in raw form in order to avoid string |
||
297 | * comparison, due to the fact that all meta values are stored as strings. |
||
298 | * |
||
299 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
300 | * @return void |
||
301 | */ |
||
302 | protected function unbindNumerics(ArgumentBag $args) |
||
316 | |||
317 | /** |
||
318 | * Get the relation constraint closure. |
||
319 | * |
||
320 | * @param string $method |
||
321 | * @param \Sofa\Hookable\Contracts\ArgumentBag $args |
||
322 | * @return \Closure |
||
323 | */ |
||
324 | protected function getMetaWhereConstraint($method, ArgumentBag $args) |
||
342 | |||
343 | /** |
||
344 | * Query Builder whereBetween override required to pass raw numeric values. |
||
345 | * |
||
346 | * @param string $column |
||
347 | * @param array $values |
||
348 | * @return \Closure |
||
349 | */ |
||
350 | protected function getMetaBetweenConstraint($column, array $values) |
||
361 | |||
362 | /** |
||
363 | * Save new or updated meta attributes and delete the ones that were unset. |
||
364 | * |
||
365 | * @return void |
||
366 | */ |
||
367 | protected function saveMeta() |
||
377 | |||
378 | /** |
||
379 | * Determine whether meta attribute is allowed for the model. |
||
380 | * |
||
381 | * @param string $key |
||
382 | * @return boolean |
||
383 | */ |
||
384 | public function allowsMeta($key) |
||
390 | |||
391 | /** |
||
392 | * Determine whether meta attribute exists on the model. |
||
393 | * |
||
394 | * @param string $key |
||
395 | * @return boolean |
||
396 | */ |
||
397 | public function hasMeta($key) |
||
401 | |||
402 | /** |
||
403 | * Get meta attribute value. |
||
404 | * |
||
405 | * @param string $key |
||
406 | * @return mixed |
||
407 | */ |
||
408 | public function getMeta($key) |
||
412 | |||
413 | /** |
||
414 | * Set meta attribute. |
||
415 | * |
||
416 | * @param string $key |
||
417 | * @param mixed $value |
||
418 | * @return void |
||
419 | */ |
||
420 | public function setMeta($key, $value) |
||
424 | |||
425 | /** |
||
426 | * Meta attributes relation. |
||
427 | * |
||
428 | * @codeCoverageIgnore |
||
429 | * |
||
430 | * @return \Illuminate\Database\Eloquent\Relations\MorphMany |
||
431 | */ |
||
432 | public function metaAttributes() |
||
436 | |||
437 | /** |
||
438 | * Get meta attributes as collection. |
||
439 | * |
||
440 | * @return \Sofa\Eloquence\Metable\AttributeBag |
||
441 | */ |
||
442 | public function getMetaAttributes() |
||
448 | |||
449 | /** |
||
450 | * Accessor for metaAttributes property |
||
451 | * |
||
452 | * @return \Sofa\Eloquence\Metable\AttributeBag |
||
453 | */ |
||
454 | public function getMetaAttributesAttribute() |
||
458 | |||
459 | /** |
||
460 | * Get meta attributes as associative array. |
||
461 | * |
||
462 | * @return array |
||
463 | */ |
||
464 | public function getMetaAttributesArray() |
||
468 | |||
469 | /** |
||
470 | * Load meta attributes relation. |
||
471 | * |
||
472 | * @return void |
||
473 | */ |
||
474 | protected function loadMetaAttributes() |
||
486 | |||
487 | /** |
||
488 | * Reload meta attributes from db or set empty bag for newly created model. |
||
489 | * |
||
490 | * @return $this |
||
491 | */ |
||
492 | protected function reloadMetaAttributes() |
||
498 | |||
499 | /** |
||
500 | * Get allowed meta attributes array. |
||
501 | * |
||
502 | * @return array |
||
503 | */ |
||
504 | public function getAllowedMeta() |
||
508 | } |
||
509 |
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.