Passed
Push — master ( 922b86...f4cc02 )
by Koen
02:28
created

HasTranslations::translationExists()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
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 6
    public static function bootHasTranslations()
32
    {
33 6
        if (config('translatable.use_saving_service', true)) {
34
            self::saving(function (self $model) {
35 6
                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 6
            });
37
38
            self::saved(function (self $model) {
39 6
                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 6
                $model->refreshTranslation();
42 6
            });
43
        }
44
45
        self::deleting(function (self $model) {
46
            $model->purgeTranslations();
47 6
        });
48
49 6
        self::addGlobalScope(new JoinTranslationScope());
50 6
    }
51
52
    /**
53
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
54
     */
55 6
    public function translations(): HasMany
56
    {
57 6
        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 4
    public function translationExists(string $locale): bool
67
    {
68 4
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->exists();
69
    }
70
71
    /**
72
     * Purge the translations.
73
     *
74
     * @return mixed
75
     */
76
    public function purgeTranslations()
77
    {
78
        return $this->translations()->delete();
79
    }
80
81
    /**
82
     * Get the translation model.
83
     *
84
     * @return string
85
     */
86 6
    public function getTranslationModel(): string
87
    {
88 6
        return property_exists($this, 'translationModel')
89
            ? $this->translationModel
90 6
            : get_class($this) . $this->getTranslationModelSuffix();
91
    }
92
93
    /**
94
     * Get the translation model suffix.
95
     *
96
     * @return string
97
     */
98 6
    protected function getTranslationModelSuffix(): string
99
    {
100 6
        return 'Translation';
101
    }
102
103
    /**
104
     * Get the translation table.
105
     *
106
     * @return string
107
     */
108 6
    public function getTranslationTable(): string
109
    {
110 6
        $model = $this->getTranslationModel();
111
112 6
        return (new $model())->getTable();
113
    }
114
115
    /**
116
     * Get the translation foreign key.
117
     *
118
     * @return string
119
     */
120 6
    public function getTranslationForeignKey()
121
    {
122 6
        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 6
    public function getTranslatable(): array
132
    {
133 6
        if (! isset($this->translatable)) {
134
            throw new MissingTranslationsException('Model "' . self::class . '" is missing translations');
135
        }
136
137 6
        return $this->translatable;
138
    }
139
140
    /**
141
     * Get the translatable attributes.
142
     *
143
     * @return array
144
     */
145 6
    public function getTranslatableAttributes(): array
146
    {
147 6
        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 6
    public function storeTranslation(string $locale, array $attributes = [])
156
    {
157 6
        if (! is_null($model = $this->translations()->where($this->getLocaleKeyName(), $locale)->first())) {
158 1
            $model->update($attributes);
159
160 1
            return $model;
161
        }
162
163 6
        $model = $this->translations()->make($attributes);
164 6
        $model->setAttribute($this->getLocaleKeyName(), $locale);
165 6
        $model->save();
166
167 6
        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
    public function getTranslation(string $locale)
190
    {
191
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->first();
192
    }
193
194
    /**
195
     * @param string $locale
196
     * @param string $name
197
     * @return mixed
198
     */
199
    public function getTranslationValue(string $locale, string $name)
200
    {
201
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->value($name);
202
    }
203
204
    /**
205
     * The locale key name.
206
     *
207
     * @return string
208
     */
209 6
    public function getLocaleKeyName(): string
210
    {
211 6
        return property_exists($this, 'localeKeyName') 
212
            ? $this->localeKeyName
213 6
            : config()->get('translatable.locale_key_name', 'locale');
214
    }
215
216
    /**
217
     * Get the locale.
218
     *
219
     * @return string
220
     */
221 6
    public function getLocale(): string
222
    {
223 6
        return null !== $this->currentLocale
224 2
            ? $this->currentLocale 
225 6
            : 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 6
    public function refreshTranslation()
234
    {
235 6
        if (! $this->exists) {
236
            return null;
237
        }
238
239 6
        $attributes = Arr::only(
240 6
            $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 6
        foreach ($attributes as $key => $value) {
244 6
            $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 6
        $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 6
        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 2
    public function translate(string $locale)
259
    {
260 2
        if (! $this->exists) {
261
            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 6
    public function formatTranslatableColumnsForSelect(): array
275
    {
276 6
        $table = $this->getTranslationTable();
277
278
        return array_map(function ($item) use ($table) {
279 6
            return $table . '.' . $item;
280 6
        }, $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 6
    public function newQueryWithoutScopes(): Builder
289
    {
290 6
        return parent::newQueryWithoutScopes()
291 6
            ->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
    public function resolveRouteBinding($value)
301
    {
302
        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