Passed
Push — master ( d2ae38...0a0160 )
by Koen
02:10
created

HasTranslations::refreshTranslation()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 17
rs 10
c 0
b 0
f 0
ccs 9
cts 9
cp 1
cc 3
nc 3
nop 0
crap 3
1
<?php declare(strict_types=1);
2
3
namespace KoenHoeijmakers\LaravelTranslatable;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Relations\HasMany;
7
use Illuminate\Support\Arr;
8
use KoenHoeijmakers\LaravelTranslatable\Exceptions\MissingTranslationsException;
9
use KoenHoeijmakers\LaravelTranslatable\Scopes\JoinTranslationScope;
10
use KoenHoeijmakers\LaravelTranslatable\Services\TranslationSavingService;
11
12
/**
13
 * Trait HasTranslations
14
 *
15
 * @mixin \Illuminate\Database\Eloquent\Model
16
 */
17
trait HasTranslations
18
{
19
    /**
20
     * The current locale, used to handle internal states.
21
     *
22
     * @var string|null
23
     */
24
    protected $currentLocale = null;
25
26
    /**
27
     * Boot the translatable trait.
28
     *
29
     * @return void
30
     */
31 16
    public static function bootHasTranslations()
32
    {
33 16
        if (config('translatable.use_saving_service', true)) {
34
            self::saving(function (self $model) {
35 11
                app(TranslationSavingService::class)->rememberTranslationForModel($model);
0 ignored issues
show
Bug introduced by
$model of type KoenHoeijmakers\LaravelT...latable\HasTranslations is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $model of KoenHoeijmakers\LaravelT...erTranslationForModel(). ( Ignorable by Annotation )

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

35
                app(TranslationSavingService::class)->rememberTranslationForModel(/** @scrutinizer ignore-type */ $model);
Loading history...
36 16
            });
37
38
            self::saved(function (self $model) {
39 11
                app(TranslationSavingService::class)->storeTranslationOnModel($model);
0 ignored issues
show
Bug introduced by
$model of type KoenHoeijmakers\LaravelT...latable\HasTranslations is incompatible with the type Illuminate\Database\Eloquent\Model expected by parameter $model of KoenHoeijmakers\LaravelT...oreTranslationOnModel(). ( Ignorable by Annotation )

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

39
                app(TranslationSavingService::class)->storeTranslationOnModel(/** @scrutinizer ignore-type */ $model);
Loading history...
40
41 11
                $model->refreshTranslation();
42 16
            });
43
        }
44
45
        self::deleting(function (self $model) {
46 1
            $model->purgeTranslations();
47 16
        });
48
49 16
        self::addGlobalScope(new JoinTranslationScope());
50 16
    }
51
52
    /**
53
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
54
     */
55 11
    public function translations(): HasMany
56
    {
57 11
        return $this->hasMany($this->getTranslationModel(), $this->getTranslationForeignKey());
0 ignored issues
show
Bug introduced by
It seems like hasMany() 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

57
        return $this->/** @scrutinizer ignore-call */ hasMany($this->getTranslationModel(), $this->getTranslationForeignKey());
Loading history...
58
    }
59
60
    /**
61
     * Check if the translation by the given locale exists.
62
     *
63
     * @param string $locale
64
     * @return bool
65
     */
66 6
    public function translationExists(string $locale): bool
67
    {
68 6
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->exists();
69
    }
70
71
    /**
72
     * Purge the translations.
73
     *
74
     * @return mixed
75
     */
76 2
    public function purgeTranslations()
77
    {
78 2
        return $this->translations()->delete();
79
    }
80
81
    /**
82
     * Get the translation model.
83
     *
84
     * @return string
85
     */
86 12
    public function getTranslationModel(): string
87
    {
88 12
        return property_exists($this, 'translationModel')
89 1
            ? $this->translationModel
90 12
            : get_class($this) . $this->getTranslationModelSuffix();
91
    }
92
93
    /**
94
     * Get the translation model suffix.
95
     *
96
     * @return string
97
     */
98 11
    protected function getTranslationModelSuffix(): string
99
    {
100 11
        return 'Translation';
101
    }
102
103
    /**
104
     * Get the translation table.
105
     *
106
     * @return string
107
     */
108 12
    public function getTranslationTable(): string
109
    {
110 12
        $model = $this->getTranslationModel();
111
112 12
        return (new $model())->getTable();
113
    }
114
115
    /**
116
     * Get the translation foreign key.
117
     *
118
     * @return string
119
     */
120 11
    public function getTranslationForeignKey()
121
    {
122 11
        return property_exists($this, 'translationForeignKey') ? $this->translationForeignKey : $this->getForeignKey();
0 ignored issues
show
Bug introduced by
It seems like getForeignKey() 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

122
        return property_exists($this, 'translationForeignKey') ? $this->translationForeignKey : $this->/** @scrutinizer ignore-call */ getForeignKey();
Loading history...
123
    }
124
125
    /**
126
     * Get the translatable.
127
     *
128
     * @return array
129
     * @throws \KoenHoeijmakers\LaravelTranslatable\Exceptions\MissingTranslationsException
130
     */
131 12
    public function getTranslatable(): array
132
    {
133 12
        if (! isset($this->translatable)) {
134 1
            throw new MissingTranslationsException('Model "' . self::class . '" is missing translations');
135
        }
136
137 11
        return $this->translatable;
138
    }
139
140
    /**
141
     * Get the translatable attributes.
142
     *
143
     * @return array
144
     */
145 11
    public function getTranslatableAttributes(): array
146
    {
147 11
        return Arr::only($this->getAttributes(), $this->translatable);
0 ignored issues
show
Bug introduced by
It seems like getAttributes() 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

147
        return Arr::only($this->/** @scrutinizer ignore-call */ getAttributes(), $this->translatable);
Loading history...
148
    }
149
150
    /**
151
     * @param string $locale
152
     * @param array<string, mixed> $attributes
153
     * @return \Illuminate\Database\Eloquent\Model
154
     */
155 11
    public function storeTranslation(string $locale, array $attributes = [])
156
    {
157 11
        if (! is_null($model = $this->translations()->where($this->getLocaleKeyName(), $locale)->first())) {
158 1
            $model->update($attributes);
159
160 1
            return $model;
161
        }
162
163 11
        $model = $this->translations()->make($attributes);
164 11
        $model->setAttribute($this->getLocaleKeyName(), $locale);
165 11
        $model->save();
166
167 11
        return $model;
168
    }
169
170
    /**
171
     * Store many translations at once.
172
     *
173
     * @param array<string, array> $translations
174
     * @return $this
175
     */
176 1
    public function storeTranslations(array $translations)
177
    {
178 1
        foreach ($translations as $locale => $translation) {
179 1
            $this->storeTranslation($locale, $translation);
180
        }
181
182 1
        return $this;
183
    }
184
185
    /**
186
     * @param string $locale
187
     * @return \Illuminate\Database\Eloquent\Model|self
188
     */
189 1
    public function getTranslation(string $locale)
190
    {
191 1
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->first();
192
    }
193
194
    /**
195
     * @param string $locale
196
     * @param string $name
197
     * @return mixed
198
     */
199 1
    public function getTranslationValue(string $locale, string $name)
200
    {
201 1
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->value($name);
202
    }
203
204
    /**
205
     * The locale key name.
206
     *
207
     * @return string
208
     */
209 12
    public function getLocaleKeyName(): string
210
    {
211 12
        return property_exists($this, 'localeKeyName') 
212 1
            ? $this->localeKeyName
213 12
            : config()->get('translatable.locale_key_name', 'locale');
214
    }
215
216
    /**
217
     * Get the locale.
218
     *
219
     * @return string
220
     */
221 11
    public function getLocale(): string
222
    {
223 11
        return null !== $this->currentLocale
224 2
            ? $this->currentLocale 
225 11
            : app()->getLocale();
0 ignored issues
show
introduced by
The method getLocale() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

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

225
            : app()->/** @scrutinizer ignore-call */ getLocale();
Loading history...
226
    }
227
228
    /**
229
     * Refresh the translation (in the current locale).
230
     *
231
     * @return \Illuminate\Database\Eloquent\Model|null|HasTranslations
232
     */
233 12
    public function refreshTranslation()
234
    {
235 12
        if (! $this->exists) {
236 1
            return null;
237
        }
238
239 11
        $attributes = Arr::only(
240 11
            $this->newQuery()->findOrFail($this->getKey())->attributes, $this->getTranslatable()
0 ignored issues
show
Bug introduced by
The method newQuery() does not exist on KoenHoeijmakers\LaravelT...latable\HasTranslations. Did you maybe mean newQueryWithoutScopes()? ( Ignorable by Annotation )

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

240
            $this->/** @scrutinizer ignore-call */ 
241
                   newQuery()->findOrFail($this->getKey())->attributes, $this->getTranslatable()

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...
Bug introduced by
It seems like getKey() 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

240
            $this->newQuery()->findOrFail($this->/** @scrutinizer ignore-call */ getKey())->attributes, $this->getTranslatable()
Loading history...
241
        );
242
243 11
        foreach ($attributes as $key => $value) {
244 11
            $this->setAttribute($key, $value);
0 ignored issues
show
Bug introduced by
It seems like setAttribute() 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

244
            $this->/** @scrutinizer ignore-call */ 
245
                   setAttribute($key, $value);
Loading history...
245
        }
246
247 11
        $this->syncOriginal();
0 ignored issues
show
Bug introduced by
It seems like syncOriginal() 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

247
        $this->/** @scrutinizer ignore-call */ 
248
               syncOriginal();
Loading history...
248
249 11
        return $this;
250
    }
251
252
    /**
253
     * Translate the model to the given locale.
254
     *
255
     * @param string $locale
256
     * @return \Illuminate\Database\Eloquent\Model|null
257
     */
258 3
    public function translate(string $locale)
259
    {
260 3
        if (! $this->exists) {
261 1
            return null;
262
        }
263
264 2
        $this->currentLocale = $locale;
265
266 2
        return $this->refreshTranslation();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->refreshTranslation() also could return the type KoenHoeijmakers\LaravelT...latable\HasTranslations which is incompatible with the documented return type Illuminate\Database\Eloquent\Model|null.
Loading history...
267
    }
268
269
    /**
270
     * Format the translated columns.
271
     *
272
     * @return array
273
     */
274 11
    public function formatTranslatableColumnsForSelect(): array
275
    {
276 11
        $table = $this->getTranslationTable();
277
278
        return array_map(function ($item) use ($table) {
279 11
            return $table . '.' . $item;
280 11
        }, $this->getTranslatable());
281
    }
282
283
    /**
284
     * Get a new query builder that doesn't have any global scopes (except the JoinTranslationScope).
285
     *
286
     * @return \Illuminate\Database\Eloquent\Builder
287
     */
288 11
    public function newQueryWithoutScopes(): Builder
289
    {
290 11
        return parent::newQueryWithoutScopes()
291 11
            ->withGlobalScope(JoinTranslationScope::class, new JoinTranslationScope());
292
    }
293
294
    /**
295
     * Retrieve the model for a bound value.
296
     *
297
     * @param  mixed $value
298
     * @return \Illuminate\Database\Eloquent\Model|null
299
     */
300 1
    public function resolveRouteBinding($value)
301
    {
302 1
        return $this->newQuery()->where($this->getTable() . '.' . $this->getRouteKeyName(), $value)->first();
0 ignored issues
show
Bug introduced by
It seems like getTable() 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

302
        return $this->newQuery()->where($this->/** @scrutinizer ignore-call */ getTable() . '.' . $this->getRouteKeyName(), $value)->first();
Loading history...
Bug introduced by
It seems like getRouteKeyName() 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

302
        return $this->newQuery()->where($this->getTable() . '.' . $this->/** @scrutinizer ignore-call */ getRouteKeyName(), $value)->first();
Loading history...
303
    }
304
}
305