This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace WeAreNeopix\LaravelModelTranslation; |
||
4 | |||
5 | use Illuminate\Support\Facades\App; |
||
6 | use Illuminate\Database\Eloquent\Model; |
||
7 | use Illuminate\Database\Eloquent\Builder; |
||
8 | use Illuminate\Database\Eloquent\SoftDeletes; |
||
9 | |||
10 | trait Translates |
||
11 | { |
||
12 | /** |
||
13 | * The language the model is currently loaded in. |
||
14 | * |
||
15 | * @var string |
||
16 | */ |
||
17 | protected $selectedLanguage; |
||
18 | |||
19 | /** |
||
20 | * Array of the model's attributes that should be translated. |
||
21 | * |
||
22 | * @var array |
||
23 | */ |
||
24 | // protected $translatable = []; |
||
25 | |||
26 | /** |
||
27 | * Array that holds the translated values while saving is performed. |
||
28 | * |
||
29 | * @var array |
||
30 | */ |
||
31 | protected $translatedAttributes = []; |
||
32 | |||
33 | /** |
||
34 | * Returns the Model identifier used for storing translations in combination with the instance identifier. |
||
35 | * |
||
36 | * @return string |
||
37 | */ |
||
38 | public function getModelIdentifier(): string |
||
39 | { |
||
40 | return static::class; |
||
41 | } |
||
42 | |||
43 | /** |
||
44 | * Returns the instance identifier used for storing translations in combination with the model identifier. |
||
45 | * |
||
46 | * @return string |
||
47 | */ |
||
48 | public function getInstanceIdentifier(): string |
||
49 | { |
||
50 | return (string) $this->getKey(); |
||
0 ignored issues
–
show
|
|||
51 | } |
||
52 | |||
53 | /** |
||
54 | * Returns the attribute name of the instance identifier. |
||
55 | * |
||
56 | * @return string |
||
57 | */ |
||
58 | protected function getInstanceIdentifierName(): string |
||
59 | { |
||
60 | return (string) $this->getKeyName(); |
||
0 ignored issues
–
show
It seems like
getKeyName() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
61 | } |
||
62 | |||
63 | /** |
||
64 | * Set the $translatable array effectively changing which attributes are translatable. |
||
65 | * It is highly recommended you avoid using this method, unless absolutely necessary. |
||
66 | * |
||
67 | * @param array $translatable |
||
68 | * @return self |
||
69 | */ |
||
70 | public function setTranslatable(array $translatable): self |
||
71 | { |
||
72 | $this->translatable = $translatable; |
||
0 ignored issues
–
show
The property
translatable does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
![]() |
|||
73 | |||
74 | return $this; |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Return all the translatable attribute names or only the ones present in the first argument. |
||
79 | * |
||
80 | * @param array $only |
||
81 | * @return array |
||
82 | */ |
||
83 | public function getTranslatable(array $only = []): array |
||
84 | { |
||
85 | return (empty($only)) |
||
86 | ? $this->translatable |
||
87 | : array_intersect($this->translatable, $only); |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * Set the current language on the instance. |
||
92 | * Automatically loads the language if it hasn't been loaded previously. |
||
93 | * |
||
94 | * @param string $language |
||
95 | * @return self |
||
96 | */ |
||
97 | public function setLanguage(string $language): self |
||
98 | { |
||
99 | $this->selectedLanguage = $language; |
||
100 | |||
101 | // We load the language automatically to avoid interferences between languages |
||
102 | $this->loadLanguage($language); |
||
103 | |||
104 | return $this; |
||
105 | } |
||
106 | |||
107 | /** |
||
108 | * Checks if a language has been previously loaded and is present in the $loadedLanguages array. |
||
109 | * |
||
110 | * @param string $language |
||
111 | * @return bool |
||
112 | */ |
||
113 | public function languageLoaded(string $language): bool |
||
114 | { |
||
115 | return $this->selectedLanguage == $language; |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Load a language and store it in the $loadedLanguages array. |
||
120 | * |
||
121 | * @param string $language |
||
122 | * @return self |
||
123 | */ |
||
124 | public function loadLanguage(string $language): array |
||
125 | { |
||
126 | $translations = Translation::getTranslationsForModel($this, $language); |
||
127 | $this->setTranslations($translations); |
||
128 | |||
129 | return $translations; |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * Set the provided data as a loaded language. |
||
134 | * |
||
135 | * @param array $translations |
||
136 | * @return self |
||
137 | */ |
||
138 | public function setTranslations(array $translations): self |
||
139 | { |
||
140 | /* |
||
141 | * We perform this translation in order to ensure |
||
142 | * that even the translations that aren't present |
||
143 | * get overridden within the attributes array |
||
144 | */ |
||
145 | $translations = collect($this->translatable)->mapWithKeys( |
||
146 | function ($attribute) use ($translations) { |
||
147 | $translation = $translations[$attribute] ?? null; |
||
148 | |||
149 | return [ |
||
150 | $attribute => $translation, |
||
151 | ]; |
||
152 | } |
||
153 | )->toArray(); |
||
154 | |||
155 | $this->attributes = array_merge($this->attributes, $translations); |
||
0 ignored issues
–
show
The property
attributes does not seem to exist. Did you mean translatedAttributes ?
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. ![]() |
|||
156 | |||
157 | return $this; |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * Reload only the languages that have already been loaded. |
||
162 | * |
||
163 | * @return self |
||
164 | */ |
||
165 | public function reloadTranslations(): self |
||
166 | { |
||
167 | $language = $this->getActiveLanguage(); |
||
168 | |||
169 | $this->loadLanguage($language); |
||
170 | |||
171 | return $this; |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Return the currently used language. Defaults to the app locale if $selectedLanguage is null. |
||
176 | * |
||
177 | * @return string |
||
178 | */ |
||
179 | public function getActiveLanguage(): string |
||
180 | { |
||
181 | return $this->selectedLanguage ?? App::getLocale(); |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * Override Laravel's refresh() method to refresh the translations as well. |
||
186 | * |
||
187 | * @return self |
||
188 | */ |
||
189 | public function refresh(): self |
||
190 | { |
||
191 | parent::refresh(); |
||
192 | |||
193 | $this->reloadTranslations(); |
||
194 | |||
195 | return $this; |
||
196 | } |
||
197 | |||
198 | /** |
||
199 | * Override Laravel's getAttribute() method to check if the requested attribute is translatable. |
||
200 | * If the attribute is translatable and the language not loaded, it will load the language. |
||
201 | * |
||
202 | * @return mixed |
||
203 | */ |
||
204 | public function getAttribute($attribute) |
||
205 | { |
||
206 | $activeLanguage = $this->getActiveLanguage(); |
||
207 | |||
208 | if ($this->attributeIsTranslatable($attribute) && ! $this->languageLoaded($activeLanguage)) { |
||
209 | $this->loadLanguage($activeLanguage); |
||
210 | } |
||
211 | |||
212 | return parent::getAttribute($attribute); |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Checks whether an attribute is translatable. |
||
217 | * |
||
218 | * @return bool |
||
219 | */ |
||
220 | public function attributeIsTranslatable($attribute) |
||
221 | { |
||
222 | return in_array($attribute, $this->translatable); |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Persist the translations using the loaded TranslationDriver. |
||
227 | * |
||
228 | * @return void |
||
229 | */ |
||
230 | protected function persistTranslations() |
||
231 | { |
||
232 | Translation::patchTranslationsForModel($this, $this->getActiveLanguage(), $this->translatedAttributes); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Merge the translated attributes with the translated ones. |
||
237 | * |
||
238 | * @return void |
||
239 | */ |
||
240 | protected function mergeTranslationsWithAttributes() |
||
241 | { |
||
242 | $this->attributes = array_merge($this->attributes, $this->translatedAttributes); |
||
0 ignored issues
–
show
The property
attributes does not seem to exist. Did you mean translatedAttributes ?
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. ![]() |
|||
243 | } |
||
244 | |||
245 | /** |
||
246 | * Separate the translatable attribute values from the $attributes array. |
||
247 | * |
||
248 | * @return array |
||
249 | */ |
||
250 | public function separateTranslationsFromAttributes(): array |
||
251 | { |
||
252 | $attributes = collect($this->attributes); |
||
0 ignored issues
–
show
The property
attributes does not seem to exist. Did you mean translatedAttributes ?
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. ![]() |
|||
253 | |||
254 | // Extract the translated values |
||
255 | $translatables = $attributes->only($this->translatable); |
||
256 | $this->translatedAttributes = $translatables->toArray(); |
||
257 | |||
258 | // Keep only the non-translatable attributes |
||
259 | $this->attributes = $attributes->forget($this->translatable)->toArray(); |
||
0 ignored issues
–
show
The property
attributes does not seem to exist. Did you mean translatedAttributes ?
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. ![]() |
|||
260 | |||
261 | return $this->translatedAttributes; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * Remove all translations from the persistent storage. |
||
266 | * This is called after the model is deleted. |
||
267 | * |
||
268 | * @param string $language |
||
0 ignored issues
–
show
There is no parameter named
$language . Did you maybe mean $languages ?
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit. Consider the following example. The parameter /**
* @param array $germany
* @param array $ireland
*/
function finale($germany, $island) {
return "2:1";
}
The most likely cause is that the parameter was changed, but the annotation was not. ![]() |
|||
269 | * @return bool |
||
270 | */ |
||
271 | public function deleteTranslations(...$languages): bool |
||
272 | { |
||
273 | if (empty($languages)) { |
||
274 | return Translation::deleteAllTranslationsForModel($this); |
||
275 | } |
||
276 | |||
277 | return Translation::deleteLanguagesForModel($this, $languages); |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * Check if the class uses the SoftDeletes trait. |
||
282 | * |
||
283 | * @return bool |
||
284 | */ |
||
285 | protected function usesSoftDelete(): bool |
||
286 | { |
||
287 | return in_array(SoftDeletes::class, class_uses_recursive(static::class)); |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * Register model event listeners which ensure that the translatable attributes are synced |
||
292 | * when the model is being saved, deleted or restored. |
||
293 | */ |
||
294 | public static function bootTranslates() |
||
295 | { |
||
296 | static::saving( |
||
297 | function (Model $model) { |
||
298 | $model->separateTranslationsFromAttributes(); |
||
299 | } |
||
300 | ); |
||
301 | |||
302 | // We have to persist the translations after the model has been saved |
||
303 | // to avoid the case when saving a new model which doesn't exist prior to saving. |
||
304 | static::saved( |
||
305 | function (Model $model) { |
||
306 | $model->persistTranslations(); |
||
307 | |||
308 | $model->mergeTranslationsWithAttributes(); |
||
309 | } |
||
310 | ); |
||
311 | |||
312 | static::deleted( |
||
313 | function (Model $model) { |
||
314 | if ($model->usesSoftDelete() && ! $model->forceDeleting) { |
||
315 | return true; |
||
316 | } |
||
317 | $model->deleteTranslations(); |
||
318 | } |
||
319 | ); |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * We make an accessor for this in order to make it possible to add it to the $appends model property. |
||
324 | * |
||
325 | * @return array |
||
326 | */ |
||
327 | public function getAvailableLanguagesAttribute() |
||
328 | { |
||
329 | return Translation::getAvailableLanguagesForModel($this); |
||
330 | } |
||
331 | |||
332 | /** |
||
333 | * We make an accessor for this in order to make it possible to add it to the $appends model property. |
||
334 | * |
||
335 | * @return string |
||
336 | */ |
||
337 | public function getSelectedLanguageAttribute() |
||
338 | { |
||
339 | return $this->getActiveLanguage(); |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Constrain a query to Models Models available in the provided language. |
||
344 | * |
||
345 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
346 | * @param string $language |
||
347 | * @return \Illuminate\Database\Eloquent\Builder |
||
348 | */ |
||
349 | public function scopeInLanguage(Builder $query, string $language) |
||
350 | { |
||
351 | $availableModelIds = Translation::getModelsAvailableInLanguage($this->getModelIdentifier(), $language); |
||
352 | $constraint = $this->getTable().'.'.$this->getInstanceIdentifierName(); |
||
0 ignored issues
–
show
It seems like
getTable() must be provided by classes using this trait. How about adding it as abstract method to this trait?
This check looks for methods that are used by a trait but not required by it. To illustrate, let’s look at the following code example trait Idable {
public function equalIds(Idable $other) {
return $this->getId() === $other->getId();
}
}
The trait Adding the ![]() |
|||
353 | |||
354 | return $query->whereIn($constraint, $availableModelIds); |
||
355 | } |
||
356 | } |
||
357 |
This check looks for methods that are used by a trait but not required by it.
To illustrate, let’s look at the following code example
The trait
Idable
provides a methodequalsId
that in turn relies on the methodgetId()
. If this method does not exist on a class mixing in this trait, the method will fail.Adding the
getId()
as an abstract method to the trait will make sure it is available.