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 |
||
11 | trait Translatable |
||
12 | { |
||
13 | protected static $autoloadTranslations = null; |
||
14 | |||
15 | protected $defaultLocale; |
||
16 | |||
17 | 480 | public static function bootTranslatable(): void |
|
18 | { |
||
19 | static::saved(function (Model $model) { |
||
20 | /* @var Translatable $model */ |
||
21 | 480 | return $model->saveTranslations(); |
|
22 | 480 | }); |
|
23 | 480 | } |
|
24 | |||
25 | /** |
||
26 | * Alias for getTranslation(). |
||
27 | * |
||
28 | * @param string|null $locale |
||
29 | * @param bool $withFallback |
||
30 | * |
||
31 | * @return \Illuminate\Database\Eloquent\Model|null |
||
32 | */ |
||
33 | 52 | public function translate($locale = null, $withFallback = false) |
|
34 | { |
||
35 | 52 | return $this->getTranslation($locale, $withFallback); |
|
36 | } |
||
37 | |||
38 | /** |
||
39 | * Alias for getTranslation(). |
||
40 | * |
||
41 | * @param string $locale |
||
42 | * |
||
43 | * @return \Illuminate\Database\Eloquent\Model|null |
||
44 | */ |
||
45 | 4 | public function translateOrDefault($locale = null) |
|
46 | { |
||
47 | 4 | return $this->getTranslation($locale, true); |
|
48 | } |
||
49 | |||
50 | /** |
||
51 | * Alias for getTranslationOrNew(). |
||
52 | * |
||
53 | * @param string $locale |
||
54 | * |
||
55 | * @return \Illuminate\Database\Eloquent\Model|null |
||
56 | */ |
||
57 | 4 | public function translateOrNew($locale = null) |
|
58 | { |
||
59 | 4 | return $this->getTranslationOrNew($locale); |
|
60 | } |
||
61 | |||
62 | /** |
||
63 | * @param string|null $locale |
||
64 | * @param bool $withFallback |
||
65 | * |
||
66 | * @return \Illuminate\Database\Eloquent\Model|null |
||
67 | */ |
||
68 | 252 | public function getTranslation($locale = null, $withFallback = null) |
|
69 | { |
||
70 | 252 | $configFallbackLocale = $this->getFallbackLocale(); |
|
71 | 252 | $locale = $locale ?: $this->locale(); |
|
72 | 252 | $withFallback = $withFallback === null ? $this->useFallback() : $withFallback; |
|
73 | 252 | $fallbackLocale = $this->getFallbackLocale($locale); |
|
74 | |||
75 | 252 | if ($translation = $this->getTranslationByLocaleKey($locale)) { |
|
76 | 144 | return $translation; |
|
77 | } |
||
78 | 172 | if ($withFallback && $fallbackLocale) { |
|
79 | 28 | if ($translation = $this->getTranslationByLocaleKey($fallbackLocale)) { |
|
80 | 16 | return $translation; |
|
81 | } |
||
82 | 12 | if ($fallbackLocale !== $configFallbackLocale && $translation = $this->getTranslationByLocaleKey($configFallbackLocale)) { |
|
83 | 8 | return $translation; |
|
84 | } |
||
85 | } |
||
86 | |||
87 | 168 | return null; |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * @param string|null $locale |
||
92 | * |
||
93 | * @return bool |
||
94 | */ |
||
95 | 12 | public function hasTranslation($locale = null) |
|
96 | { |
||
97 | 12 | $locale = $locale ?: $this->locale(); |
|
98 | |||
99 | 12 | foreach ($this->translations as $translation) { |
|
|
|||
100 | 4 | if ($translation->getAttribute($this->getLocaleKey()) == $locale) { |
|
101 | 4 | return true; |
|
102 | } |
||
103 | } |
||
104 | |||
105 | 12 | return false; |
|
106 | } |
||
107 | |||
108 | /** |
||
109 | * @return string |
||
110 | */ |
||
111 | 316 | public function getTranslationModelName() |
|
112 | { |
||
113 | 316 | return $this->translationModel ?: $this->getTranslationModelNameDefault(); |
|
114 | } |
||
115 | |||
116 | /** |
||
117 | * @return string |
||
118 | */ |
||
119 | 304 | public function getTranslationModelNameDefault() |
|
120 | { |
||
121 | 304 | $modelName = get_class($this); |
|
122 | |||
123 | 304 | if ($namespace = $this->getTranslationModelNamespace()) { |
|
124 | 4 | $modelName = $namespace.'\\'.class_basename(get_class($this)); |
|
125 | } |
||
126 | |||
127 | 304 | return $modelName.config('translatable.translation_suffix', 'Translation'); |
|
128 | } |
||
129 | |||
130 | /** |
||
131 | * @return string|null |
||
132 | */ |
||
133 | 304 | public function getTranslationModelNamespace() |
|
134 | { |
||
135 | 304 | return config('translatable.translation_model_namespace'); |
|
136 | } |
||
137 | |||
138 | /** |
||
139 | * @return string |
||
140 | */ |
||
141 | 316 | public function getRelationKey() |
|
142 | { |
||
143 | 316 | if ($this->translationForeignKey) { |
|
144 | 28 | return $this->translationForeignKey; |
|
145 | } |
||
146 | |||
147 | 292 | return $this->getForeignKey(); |
|
148 | } |
||
149 | |||
150 | /** |
||
151 | * @return string |
||
152 | */ |
||
153 | 296 | public function getLocaleKey() |
|
154 | { |
||
155 | 296 | return $this->localeKey ?: config('translatable.locale_key', 'locale'); |
|
156 | } |
||
157 | |||
158 | /** |
||
159 | * @return \Illuminate\Database\Eloquent\Relations\HasMany |
||
160 | */ |
||
161 | 296 | public function translations() |
|
162 | { |
||
163 | 296 | return $this->hasMany($this->getTranslationModelName(), $this->getRelationKey()); |
|
164 | } |
||
165 | |||
166 | /** |
||
167 | * @return bool |
||
168 | */ |
||
169 | 16 | private function usePropertyFallback() |
|
170 | { |
||
171 | 16 | return $this->useFallback() && config('translatable.use_property_fallback', false); |
|
172 | } |
||
173 | |||
174 | /** |
||
175 | * Returns the attribute value from fallback translation if value of attribute |
||
176 | * is empty and the property fallback is enabled in the configuration. |
||
177 | * in model. |
||
178 | * @param $locale |
||
179 | * @param $attribute |
||
180 | * @return mixed |
||
181 | */ |
||
182 | 96 | private function getAttributeOrFallback($locale, $attribute) |
|
183 | { |
||
184 | 96 | $translation = $this->getTranslation($locale); |
|
185 | |||
186 | if ( |
||
187 | ( |
||
188 | 96 | ! $translation instanceof Model || |
|
189 | 96 | $this->isEmptyTranslatableAttribute($attribute, $translation->$attribute) |
|
190 | ) && |
||
191 | 96 | $this->usePropertyFallback() |
|
192 | ) { |
||
193 | 12 | $translation = $this->getTranslation($this->getFallbackLocale(), false); |
|
194 | } |
||
195 | |||
196 | 96 | if ($translation instanceof Model) { |
|
197 | 92 | return $translation->$attribute; |
|
198 | } |
||
199 | |||
200 | 8 | return null; |
|
201 | } |
||
202 | |||
203 | 88 | protected function isEmptyTranslatableAttribute(string $key, $value): bool |
|
204 | { |
||
205 | 88 | return empty($value); |
|
206 | } |
||
207 | |||
208 | /** |
||
209 | * @param string $key |
||
210 | * |
||
211 | * @return mixed |
||
212 | */ |
||
213 | 480 | public function getAttribute($key) |
|
236 | |||
237 | /** |
||
238 | * @param string $key |
||
239 | * @param mixed $value |
||
240 | * |
||
241 | * @return $this |
||
242 | */ |
||
243 | 480 | public function setAttribute($key, $value) |
|
255 | |||
256 | /** |
||
257 | * @param string $locale |
||
258 | * |
||
259 | * @return \Illuminate\Database\Eloquent\Model |
||
260 | */ |
||
261 | 144 | protected function getTranslationOrNew($locale = null) |
|
271 | |||
272 | /** |
||
273 | * @param array $attributes |
||
274 | * |
||
275 | * @throws \Illuminate\Database\Eloquent\MassAssignmentException |
||
276 | * @return $this |
||
277 | */ |
||
278 | 480 | public function fill(array $attributes) |
|
295 | |||
296 | /** |
||
297 | * @param string $key |
||
298 | */ |
||
299 | 252 | private function getTranslationByLocaleKey($key) |
|
309 | |||
310 | /** |
||
311 | * @param null $locale |
||
312 | * |
||
313 | * @return string |
||
314 | */ |
||
315 | 256 | private function getFallbackLocale($locale = null) |
|
325 | |||
326 | 252 | private function isLocaleCountryBased(string $locale): bool |
|
327 | { |
||
328 | 252 | return $this->getLocalesHelper()->isLocaleCountryBased($locale); |
|
329 | } |
||
330 | |||
331 | 28 | private function getLanguageFromCountryBasedLocale(string $locale): string |
|
332 | { |
||
333 | 28 | return $this->getLocalesHelper()->getLanguageFromCountryBasedLocale($locale); |
|
334 | } |
||
335 | |||
336 | /** |
||
337 | * @return bool|null |
||
338 | */ |
||
339 | 148 | private function useFallback() |
|
340 | { |
||
341 | 148 | if (isset($this->useTranslationFallback) && $this->useTranslationFallback !== null) { |
|
342 | 12 | return $this->useTranslationFallback; |
|
347 | |||
348 | /** |
||
349 | * @param string $key |
||
350 | * |
||
351 | * @return bool |
||
352 | */ |
||
353 | 480 | public function isTranslationAttribute($key) |
|
357 | |||
358 | 112 | protected function isKeyALocale(string $key): bool |
|
362 | |||
363 | protected function getLocales(): array |
||
367 | |||
368 | protected function getLocaleSeparator(): string |
||
372 | |||
373 | /** |
||
374 | * @return bool |
||
375 | */ |
||
376 | 480 | protected function saveTranslations() |
|
397 | |||
398 | /** |
||
399 | * @param array |
||
400 | * |
||
401 | * @return \Illuminate\Database\Eloquent\Model |
||
402 | */ |
||
403 | 4 | public function replicateWithTranslations(array $except = null) |
|
415 | |||
416 | /** |
||
417 | * @param \Illuminate\Database\Eloquent\Model $translation |
||
418 | * |
||
419 | * @return bool |
||
420 | */ |
||
421 | 104 | protected function isTranslationDirty(Model $translation) |
|
428 | |||
429 | /** |
||
430 | * @param string $locale |
||
431 | * |
||
432 | * @return \Illuminate\Database\Eloquent\Model |
||
433 | */ |
||
434 | 132 | public function getNewTranslation($locale) |
|
443 | |||
444 | /** |
||
445 | * @param $key |
||
446 | * |
||
447 | * @return bool |
||
448 | */ |
||
449 | 152 | 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 | 8 | 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 | 8 | 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 | 4 | public function scopeTranslated(Builder $query) |
|
493 | |||
494 | /** |
||
495 | * Adds scope to get a list of translated attributes, using the current locale. |
||
496 | * Example usage: Country::listsTranslations('name')->get()->toArray() |
||
497 | * Will return an array with items: |
||
498 | * [ |
||
499 | * 'id' => '1', // The id of country |
||
500 | * 'name' => 'Griechenland' // The translated name |
||
501 | * ]. |
||
502 | * |
||
503 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
504 | * @param string $translationField |
||
505 | */ |
||
506 | 12 | public function scopeListsTranslations(Builder $query, $translationField) |
|
530 | |||
531 | /** |
||
532 | * This scope eager loads the translations for the default and the fallback locale only. |
||
533 | * We can use this as a shortcut to improve performance in our application. |
||
534 | * |
||
535 | * @param Builder $query |
||
536 | */ |
||
537 | 12 | public function scopeWithTranslation(Builder $query) |
|
553 | |||
554 | /** |
||
555 | * This scope filters results by checking the translation fields. |
||
556 | * |
||
557 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
558 | * @param string $key |
||
559 | * @param string $value |
||
560 | * @param string $locale |
||
561 | * |
||
562 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
563 | */ |
||
564 | 12 | public function scopeWhereTranslation(Builder $query, $key, $value, $locale = null) |
|
573 | |||
574 | /** |
||
575 | * This scope filters results by checking the translation fields. |
||
576 | * |
||
577 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
578 | * @param string $key |
||
579 | * @param string $value |
||
580 | * @param string $locale |
||
581 | * |
||
582 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
583 | */ |
||
584 | 4 | public function scopeOrWhereTranslation(Builder $query, $key, $value, $locale = null) |
|
593 | |||
594 | /** |
||
595 | * This scope filters results by checking the translation fields. |
||
596 | * |
||
597 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
598 | * @param string $key |
||
599 | * @param string $value |
||
600 | * @param string $locale |
||
601 | * |
||
602 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
603 | */ |
||
604 | 12 | public function scopeWhereTranslationLike(Builder $query, $key, $value, $locale = null) |
|
613 | |||
614 | /** |
||
615 | * This scope filters results by checking the translation fields. |
||
616 | * |
||
617 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
618 | * @param string $key |
||
619 | * @param string $value |
||
620 | * @param string $locale |
||
621 | * |
||
622 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
623 | */ |
||
624 | 4 | public function scopeOrWhereTranslationLike(Builder $query, $key, $value, $locale = null) |
|
633 | |||
634 | /** |
||
635 | * This scope sorts results by the given translation field. |
||
636 | * |
||
637 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
638 | * @param string $key |
||
639 | * @param string $sortmethod |
||
640 | * |
||
641 | * @return \Illuminate\Database\Eloquent\Builder|static |
||
642 | */ |
||
643 | 8 | public function scopeOrderByTranslation(Builder $query, $key, $sortmethod = 'asc') |
|
660 | |||
661 | /** |
||
662 | * @return array |
||
663 | */ |
||
664 | 48 | public function attributesToArray() |
|
687 | |||
688 | /** |
||
689 | * @return array |
||
690 | */ |
||
691 | 4 | public function getTranslationsArray() |
|
703 | |||
704 | /** |
||
705 | * @return string |
||
706 | */ |
||
707 | 56 | private function getTranslationsTable() |
|
711 | |||
712 | 480 | protected function locale(): string |
|
720 | |||
721 | /** |
||
722 | * Set the default locale on the model. |
||
723 | * |
||
724 | * @param $locale |
||
725 | * |
||
726 | * @return $this |
||
727 | */ |
||
728 | 4 | public function setDefaultLocale($locale) |
|
734 | |||
735 | /** |
||
736 | * Get the default locale on the model. |
||
737 | * |
||
738 | * @return mixed |
||
739 | */ |
||
740 | public function getDefaultLocale() |
||
744 | |||
745 | /** |
||
746 | * Deletes all translations for this model. |
||
747 | * |
||
748 | * @param string|array|null $locales The locales to be deleted (array or single string) |
||
749 | * (e.g., ["en", "de"] would remove these translations). |
||
750 | */ |
||
751 | 12 | public function deleteTranslations($locales = null) |
|
767 | |||
768 | /** |
||
769 | * @param $key |
||
770 | * |
||
771 | * @return array |
||
772 | */ |
||
773 | 480 | private function getAttributeAndLocale($key) |
|
781 | |||
782 | /** |
||
783 | * @return bool |
||
784 | */ |
||
785 | 32 | private function toArrayAlwaysLoadsTranslations() |
|
789 | |||
790 | public static function enableAutoloadTranslations() |
||
794 | |||
795 | 4 | public static function defaultAutoloadTranslations() |
|
799 | |||
800 | 4 | public static function disableAutoloadTranslations() |
|
804 | |||
805 | 480 | protected function getLocalesHelper(): Locales |
|
809 | } |
||
810 |
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.