Issues (238)

src/Traits/HasTranslations.php (11 issues)

1
<?php
2
3
namespace Translation\Traits;
4
5
use Illuminate\Support\Facades\Config;
6
use Illuminate\Support\Facades\Lang;
7
use Illuminate\Support\Str;
8
use Translation\Events\TranslationHasBeenSet;
9
use Translation\Exceptions\AttributeIsNotTranslatable;
10
use Translation\Repositories\ModelTranslationRepository;
11
use Translation\Services\GoogleTranslate;
12
13
/**
14
 * Model has Translations
15
 *
16
 * Usage: Add in model
17
 *  use HasTranslations;
18
 *
19
 *  public $translatable = ['name'];
20
 */
21
trait HasTranslations
22
{
23
    use Translatable;
24
    // @todo retirado tava dando pau, veio do cms
25
    // protected $appends = [
26
    //     'translations',
27
    // ];
28
29
    /**
30
     * From Siravel
31
32
     */
33
34
    // /**
35
    //  * Get a translation.
36
    //  *
37
    //  * @param string $lang
38
    //  *
39
    //  * @return mixed
40
    //  */
41
    // public function translation($lang)
42
    // {
43
    //     return app(ModelTranslationRepository::class)->getTranslation($this->id, get_class($this), $lang);
44
    // }
45
46
    /**
47
     * Get translation data.
48
     *
49
     * @param string $lang
50
     *
51
     * @return array|null
52
     */
53
    public function translationData($lang)
54
    {
55
        $translation = $this->translation($lang);
56
57
        if ($translation) {
58
            return json_decode($translation->entity_data);
59
        }
60
61
        return null;
62
    }
63
    
64
    /**
65
     * Peguei da Spatie
66
     */
67
    public function getAttributeValue($key)
68
    {
69
        if (! $this->isTranslatableAttribute($key)) {
70
            return parent::getAttributeValue($key);
71
        }
72
73
        return $this->getTranslation($key, $this->getLocale());
74
    }
75
76
    public function setAttribute($key, $value)
77
    {
78
        // Pass arrays and untranslatable attributes to the parent method.
79
        if (! $this->isTranslatableAttribute($key) || is_array($value)) {
80
            return parent::setAttribute($key, $value);
81
        }
82
83
        // If the attribute is translatable and not already translated, set a
84
        // translation for the current app locale.
85
        return $this->setTranslation($key, $this->getLocale(), $value);
86
    }
87
88
    public function translate(string $key, string $locale = '', bool $useFallbackLocale = true): string
89
    {
90
        return $this->getTranslation($key, $locale, $useFallbackLocale);
91
    }
92
93
    public function getTranslation(string $key, string $locale, bool $useFallbackLocale = true)
94
    {
95
        $locale = $this->normalizeLocale($key, $locale, $useFallbackLocale);
96
97
        $translations = $this->getTranslations($key);
98
99
        $translation = $translations[$locale] ?? '';
100
101
        if ($this->hasGetMutator($key)) {
0 ignored issues
show
It seems like hasGetMutator() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

101
        if ($this->/** @scrutinizer ignore-call */ hasGetMutator($key)) {
Loading history...
102
            return $this->mutateAttribute($key, $translation);
0 ignored issues
show
It seems like mutateAttribute() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

102
            return $this->/** @scrutinizer ignore-call */ mutateAttribute($key, $translation);
Loading history...
103
        }
104
105
        return $translation;
106
    }
107
108
    public function getTranslationWithFallback(string $key, string $locale): string
109
    {
110
        return $this->getTranslation($key, $locale, true);
111
    }
112
113
    public function getTranslationWithoutFallback(string $key, string $locale)
114
    {
115
        return $this->getTranslation($key, $locale, false);
116
    }
117
    public function getTranslatedAttribute(string $key = null): string
118
    {
119
        return $this->getTranslation($key, $this->getLocale());
0 ignored issues
show
It seems like $key can also be of type null; however, parameter $key of Translation\Traits\HasTr...tions::getTranslation() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
        return $this->getTranslation(/** @scrutinizer ignore-type */ $key, $this->getLocale());
Loading history...
120
    }
121
    public function getTranslations(string $key = null): array
122
    {
123
        if ($key !== null) {
124
            $this->guardAgainstNonTranslatableAttribute($key);
125
126
            return array_filter(
127
                json_decode($this->getAttributes()[$key] ?? '' ?: '{}', true) ?: [],
0 ignored issues
show
The method getAttributes() does not exist on Translation\Traits\HasTranslations. Did you maybe mean getAttributeValue()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

127
                json_decode($this->/** @scrutinizer ignore-call */ getAttributes()[$key] ?? '' ?: '{}', true) ?: [],

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
128
                function ($value) {
129
                    return $value !== null && $value !== '';
130
                }
131
            );
132
        }
133
134
        return array_reduce(
135
            $this->getTranslatableAttributes(),
136
            function ($result, $item) {
137
                $result[$item] = $this->getTranslations($item);
138
139
                return $result;
140
            }
141
        );
142
    }
143
144
    public function setTranslation(string $key, string $locale, $value)//: HasTranslations
145
    {
146
        $this->guardAgainstNonTranslatableAttribute($key);
147
148
        $translations = $this->getTranslations($key);
149
150
        $oldValue = $translations[$locale] ?? '';
151
152
        if ($this->hasSetMutator($key)) {
0 ignored issues
show
It seems like hasSetMutator() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

152
        if ($this->/** @scrutinizer ignore-call */ hasSetMutator($key)) {
Loading history...
153
            $method = 'set'.Str::studly($key).'Attribute';
154
155
            $this->{$method}($value, $locale);
156
157
            $value = $this->attributes[$key];
158
        }
159
160
        $translations[$locale] = $value;
161
162
        $this->attributes[$key] = $this->asJson($translations);
0 ignored issues
show
Bug Best Practice introduced by
The property attributes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
It seems like asJson() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

162
        /** @scrutinizer ignore-call */ 
163
        $this->attributes[$key] = $this->asJson($translations);
Loading history...
163
164
        event(new TranslationHasBeenSet($this, $key, $locale, $oldValue, $value));
0 ignored issues
show
The function event was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
        /** @scrutinizer ignore-call */ 
165
        event(new TranslationHasBeenSet($this, $key, $locale, $oldValue, $value));
Loading history...
165
166
        return $this;
167
    }
168
169
    public function setTranslations(string $key, array $translations)//: HasTranslations
170
    {
171
        $this->guardAgainstNonTranslatableAttribute($key);
172
173
        foreach ($translations as $locale => $translation) {
174
            $this->setTranslation($key, $locale, $translation);
175
        }
176
177
        return $this;
178
    }
179
180
    public function forgetTranslation(string $key, string $locale)//: HasTranslations
181
    {
182
        $translations = $this->getTranslations($key);
183
184
        unset(
185
            $translations[$locale],
186
            $this->$key
187
        );
188
189
        $this->setTranslations($key, $translations);
190
191
        return $this;
192
    }
193
194
    public function forgetAllTranslations(string $locale)//: HasTranslations
195
    {
196
        collect($this->getTranslatableAttributes())->each(
197
            function (string $attribute) use ($locale) {
198
                $this->forgetTranslation($attribute, $locale);
199
            }
200
        );
201
202
        return $this;
203
    }
204
205
    public function getTranslatedLocales(string $key): array
206
    {
207
        return array_keys($this->getTranslations($key));
208
    }
209
210
    public function isTranslatableAttribute(string $key): bool
211
    {
212
        return in_array($key, $this->getTranslatableAttributes());
213
    }
214
215
    public function hasTranslation(string $key, string $locale = null): bool
216
    {
217
        $locale = $locale ?: $this->getLocale();
218
219
        return isset($this->getTranslations($key)[$locale]);
220
    }
221
222
    protected function guardAgainstNonTranslatableAttribute(string $key)
223
    {
224
        if (! $this->isTranslatableAttribute($key)) {
225
            throw AttributeIsNotTranslatable::make($key, $this);
226
        }
227
    }
228
229
    protected function normalizeLocale(string $key, string $locale, bool $useFallbackLocale): string
230
    {
231
        if (in_array($locale, $this->getTranslatedLocales($key))) {
232
            return $locale;
233
        }
234
235
        if (! $useFallbackLocale) {
236
            return $locale;
237
        }
238
239
        if (! is_null($fallbackLocale = config('translatable.fallback_locale'))) {
0 ignored issues
show
The call to config() has too few arguments starting with defaultValue. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

239
        if (! is_null($fallbackLocale = /** @scrutinizer ignore-call */ config('translatable.fallback_locale'))) {

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
240
            return $fallbackLocale;
241
        }
242
243
        if (! is_null($fallbackLocale = config('app.fallback_locale'))) {
244
            return $fallbackLocale;
245
        }
246
247
        return $locale;
248
    }
249
250
    protected function getLocale(): string
251
    {
252
        return config('app.locale');
0 ignored issues
show
The call to config() has too few arguments starting with defaultValue. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

252
        return /** @scrutinizer ignore-call */ config('app.locale');

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
253
    }
254
255
    public function getTranslatableAttributes(): array
256
    {
257
        if (!property_exists($this, 'translatable')) {
258
            return [];
259
        }
260
        return is_array($this->translatable)
261
            ? $this->translatable
262
            : [];
263
    }
264
265
266
    /**
267
     * Get a translations attribute.
268
     *
269
     * @return array
270
     */
271
    public function getTranslationsAttribute(): array
272
    {
273
        return collect($this->getTranslatableAttributes())
274
            ->mapWithKeys(
275
                function (string $key) {
276
                    return [$key => $this->getTranslations($key)];
277
                }
278
            )
279
            ->toArray();
280
    }
281
282
    /**
283
     * Veio do Siravel @todo
284
     *
285
     * @return array
286
     * public function getTranslationsAttribute()
287
     * {
288
     *     $translationData = [];
289
     *     $translations = ModelTranslation::where('entity_id', $this->id)->where('entity_type', get_class($this))->get();
290
     *
291
     *     foreach ($translations as $translation) {
292
     *         $translationData[] = $translation->data->attributes;
293
     *     }
294
     *
295
     *     return $translationData;
296
     * }
297
     */
298
299
    public function getCasts(): array
300
    {
301
        return array_merge(
302
            parent::getCasts(),
303
            array_fill_keys($this->getTranslatableAttributes(), 'array')
304
        );
305
    }
306
307
308
    /**
309
     * Get a model as a translatable object (From CMS)
310
     *
311
     * @return Object
312
     */
313
    public function asObject()
314
    {
315
        if (! is_null(request('lang')) && request('lang') !== config('cms.default-language', 'en')) {
0 ignored issues
show
The function request was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

315
        if (! is_null(/** @scrutinizer ignore-call */ request('lang')) && request('lang') !== config('cms.default-language', 'en')) {
Loading history...
316
            return $this->translationData(request('lang'));
317
        }
318
319
        return $this;
320
    }
321
}
322