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 Translatable 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 Translatable, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | trait Translatable |
||
14 | { |
||
15 | /** |
||
16 | * Alias for getTranslation(). |
||
17 | * |
||
18 | * @param string|null $locale |
||
19 | * @param bool $withFallback |
||
20 | * |
||
21 | * @return \Illuminate\Database\Eloquent\Model|null |
||
22 | */ |
||
23 | public function translate($locale = null, $withFallback = false) |
||
27 | |||
28 | /** |
||
29 | * Alias for getTranslation(). |
||
30 | * |
||
31 | * @param string $locale |
||
32 | * |
||
33 | * @return \Illuminate\Database\Eloquent\Model|null |
||
34 | */ |
||
35 | public function translateOrDefault($locale) |
||
39 | |||
40 | /** |
||
41 | * Alias for getTranslationOrNew(). |
||
42 | * |
||
43 | * @param string $locale |
||
44 | * |
||
45 | * @return \Illuminate\Database\Eloquent\Model|null |
||
46 | */ |
||
47 | public function translateOrNew($locale) |
||
51 | |||
52 | /** |
||
53 | * @param string|null $locale |
||
54 | * @param bool $withFallback |
||
55 | * |
||
56 | * @return \Illuminate\Database\Eloquent\Model|null |
||
57 | */ |
||
58 | public function getTranslation($locale = null, $withFallback = null) |
||
79 | |||
80 | /** |
||
81 | * @param string|null $locale |
||
82 | * |
||
83 | * @return bool |
||
84 | */ |
||
85 | public function hasTranslation($locale = null) |
||
97 | |||
98 | /** |
||
99 | * @return string |
||
100 | */ |
||
101 | public function getTranslationModelName() |
||
105 | |||
106 | /** |
||
107 | * @return string |
||
108 | */ |
||
109 | public function getTranslationModelNameDefault() |
||
115 | |||
116 | /** |
||
117 | * @return string |
||
118 | */ |
||
119 | public function getRelationKey() |
||
131 | |||
132 | /** |
||
133 | * @return string |
||
134 | */ |
||
135 | public function getLocaleKey() |
||
136 | { |
||
137 | $config = app()->make('config'); |
||
138 | |||
139 | return $this->localeKey ?: $config->get('translatable.locale_key', 'locale'); |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * @return \Illuminate\Database\Eloquent\Relations\HasMany |
||
144 | */ |
||
145 | public function translations() |
||
149 | |||
150 | /** |
||
151 | * @param string $key |
||
152 | * |
||
153 | * @return mixed |
||
154 | */ |
||
155 | public function getAttribute($key) |
||
182 | |||
183 | /** |
||
184 | * @param string $key |
||
185 | * @param mixed $value |
||
186 | */ |
||
187 | public function setAttribute($key, $value) |
||
203 | |||
204 | /** |
||
205 | * @param array $options |
||
206 | * |
||
207 | * @return bool |
||
208 | */ |
||
209 | public function save(array $options = []) |
||
237 | |||
238 | /** |
||
239 | * @param string $locale |
||
240 | * |
||
241 | * @return \Illuminate\Database\Eloquent\Model|null |
||
242 | */ |
||
243 | protected function getTranslationOrNew($locale) |
||
251 | |||
252 | /** |
||
253 | * @param array $attributes |
||
254 | * |
||
255 | * @throws \Illuminate\Database\Eloquent\MassAssignmentException |
||
256 | * |
||
257 | * @return $this |
||
258 | */ |
||
259 | public function fill(array $attributes) |
||
274 | |||
275 | /** |
||
276 | * @param string $key |
||
277 | */ |
||
278 | private function getTranslationByLocaleKey($key) |
||
288 | |||
289 | /** |
||
290 | * @param null $locale |
||
291 | * |
||
292 | * @return string |
||
293 | */ |
||
294 | private function getFallbackLocale($locale = null) |
||
304 | |||
305 | /** |
||
306 | * @param $locale |
||
307 | * |
||
308 | * @return bool |
||
309 | */ |
||
310 | private function isLocaleCountryBased($locale) |
||
314 | |||
315 | /** |
||
316 | * @param $locale |
||
317 | * |
||
318 | * @return string |
||
319 | */ |
||
320 | private function getLanguageFromCountryBasedLocale($locale) |
||
326 | |||
327 | /** |
||
328 | * @return bool|null |
||
329 | */ |
||
330 | private function useFallback() |
||
338 | |||
339 | /** |
||
340 | * @param string $key |
||
341 | * |
||
342 | * @return bool |
||
343 | */ |
||
344 | public function isTranslationAttribute($key) |
||
348 | |||
349 | /** |
||
350 | * @param string $key |
||
351 | * |
||
352 | * @throws \Dimsav\Translatable\Exception\LocalesNotDefinedException |
||
353 | * |
||
354 | * @return bool |
||
355 | */ |
||
356 | protected function isKeyALocale($key) |
||
362 | |||
363 | /** |
||
364 | * @throws \Dimsav\Translatable\Exception\LocalesNotDefinedException |
||
365 | * |
||
366 | * @return array |
||
367 | */ |
||
368 | protected function getLocales() |
||
391 | |||
392 | /** |
||
393 | * @return string |
||
394 | */ |
||
395 | protected function getLocaleSeparator() |
||
399 | |||
400 | /** |
||
401 | * @return bool |
||
402 | */ |
||
403 | protected function saveTranslations() |
||
415 | |||
416 | /** |
||
417 | * @param \Illuminate\Database\Eloquent\Model $translation |
||
418 | * |
||
419 | * @return bool |
||
420 | */ |
||
421 | protected function isTranslationDirty(Model $translation) |
||
428 | |||
429 | /** |
||
430 | * @param string $locale |
||
431 | * |
||
432 | * @return \Illuminate\Database\Eloquent\Model |
||
433 | */ |
||
434 | public function getNewTranslation($locale) |
||
443 | |||
444 | /** |
||
445 | * @param $key |
||
446 | * |
||
447 | * @return bool |
||
448 | */ |
||
449 | public function __isset($key) |
||
453 | |||
454 | /** |
||
455 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
456 | * @param string $locale |
||
457 | * |
||
458 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
459 | */ |
||
460 | View Code Duplication | public function scopeTranslatedIn(Builder $query, $locale = null) |
|
468 | |||
469 | /** |
||
470 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
471 | * @param string $locale |
||
472 | * |
||
473 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
474 | */ |
||
475 | View Code Duplication | public function scopeNotTranslatedIn(Builder $query, $locale = null) |
|
483 | |||
484 | /** |
||
485 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
486 | * |
||
487 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
488 | */ |
||
489 | public function scopeTranslated(Builder $query) |
||
493 | |||
494 | /** |
||
495 | * Adds scope to get a list of translated attributes, using the current locale. |
||
496 | * |
||
497 | * Example usage: Country::listsTranslations('name')->get()->toArray() |
||
498 | * Will return an array with items: |
||
499 | * [ |
||
500 | * 'id' => '1', // The id of country |
||
501 | * 'name' => 'Griechenland' // The translated name |
||
502 | * ] |
||
503 | * |
||
504 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
505 | * @param string $translationField |
||
506 | */ |
||
507 | public function scopeListsTranslations(Builder $query, $translationField) |
||
528 | |||
529 | /** |
||
530 | * This scope eager loads the translations for the default and the fallback locale only. |
||
531 | * We can use this as a shortcut to improve performance in our application. |
||
532 | * |
||
533 | * @param Builder $query |
||
534 | */ |
||
535 | public function scopeWithTranslation(Builder $query) |
||
545 | |||
546 | /** |
||
547 | * This scope filters results by checking the translation fields. |
||
548 | * |
||
549 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
550 | * @param string $key |
||
551 | * @param string $value |
||
552 | * @param string $locale |
||
553 | * |
||
554 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
555 | */ |
||
556 | View Code Duplication | public function scopeWhereTranslation(Builder $query, $key, $value, $locale = null) |
|
565 | |||
566 | /** |
||
567 | * This scope filters results by checking the translation fields. |
||
568 | * |
||
569 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
570 | * @param string $key |
||
571 | * @param string $value |
||
572 | * @param string $locale |
||
573 | * |
||
574 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
575 | */ |
||
576 | View Code Duplication | public function scopeWhereTranslationLike(Builder $query, $key, $value, $locale = null) |
|
585 | |||
586 | /** |
||
587 | * @return array |
||
588 | */ |
||
589 | public function toArray() |
||
607 | |||
608 | /** |
||
609 | * @return bool |
||
610 | */ |
||
611 | private function alwaysFillable() |
||
615 | |||
616 | /** |
||
617 | * @return string |
||
618 | */ |
||
619 | private function getTranslationsTable() |
||
623 | |||
624 | /** |
||
625 | * @return string |
||
626 | */ |
||
627 | protected function locale() |
||
632 | } |
||
633 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.