Passed
Pull Request — master (#3)
by Koen
03:18
created

HasTranslations::getTranslation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace KoenHoeijmakers\LaravelTranslatable;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Support\Arr;
7
use KoenHoeijmakers\LaravelTranslatable\Exceptions\MissingTranslationsException;
8
use KoenHoeijmakers\LaravelTranslatable\Scopes\JoinTranslationScope;
9
use KoenHoeijmakers\LaravelTranslatable\Services\TranslationSavingService;
10
11
/**
12
 * Trait HasTranslations
13
 *
14
 * @mixin \Illuminate\Database\Eloquent\Model
15
 */
16
trait HasTranslations
17
{
18
    /**
19
     * The current locale, used to handle internal states.
20
     *
21
     * @var string|null
22
     */
23
    protected $currentLocale = null;
24
25
    /**
26
     * Boot the translatable trait.
27
     *
28
     * @return void
29
     */
30
    public static function bootHasTranslations()
31
    {
32
        if (config('translatable.use_saving_service', true)) {
33
            static::saving(function (self $model) {
34
                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

34
                app(TranslationSavingService::class)->rememberTranslationForModel(/** @scrutinizer ignore-type */ $model);
Loading history...
35
            });
36
37
            static::saved(function (self $model) {
38
                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

38
                app(TranslationSavingService::class)->storeTranslationOnModel(/** @scrutinizer ignore-type */ $model);
Loading history...
39
40
                $model->refreshTranslation();
41
            });
42
        }
43
44
        static::deleting(function (self $model) {
45
            $model->purgeTranslations();
46
        });
47
48
        static::addGlobalScope(new JoinTranslationScope());
49
    }
50
51
    /**
52
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
53
     */
54
    public function translations()
55
    {
56
        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

56
        return $this->/** @scrutinizer ignore-call */ hasMany($this->getTranslationModel(), $this->getTranslationForeignKey());
Loading history...
57
    }
58
59
    /**
60
     * Check if the translation by the given locale exists.
61
     *
62
     * @param string $locale
63
     * @return bool
64
     */
65
    public function translationExists(string $locale): bool
66
    {
67
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->exists();
68
    }
69
70
    /**
71
     * Get all the translations.
72
     *
73
     * @return \Illuminate\Database\Eloquent\Collection
74
     */
75
    public function getTranslations()
76
    {
77
        return $this->translations()->get();
78
    }
79
80
    /**
81
     * Purge the translations.
82
     *
83
     * @return mixed
84
     */
85
    public function purgeTranslations()
86
    {
87
        return $this->getTranslations()->each->delete();
88
    }
89
90
    /**
91
     * Get the translation model.
92
     *
93
     * @return string
94
     */
95
    public function getTranslationModel(): string
96
    {
97
        if (isset($this->translationModel)) {
98
            return $this->translationModel;
99
        }
100
101
        return get_class($this) . $this->getTranslationModelSuffix();
102
    }
103
104
    /**
105
     * Get the translation model suffix.
106
     *
107
     * @return string
108
     */
109
    protected function getTranslationModelSuffix(): string
110
    {
111
        return 'Translation';
112
    }
113
114
    /**
115
     * Get the translation table.
116
     *
117
     * @return string
118
     */
119
    public function getTranslationTable(): string
120
    {
121
        $model = $this->getTranslationModel();
122
123
        return (new $model())->getTable();
124
    }
125
126
    /**
127
     * Get the translation foreign key.
128
     *
129
     * @return string
130
     */
131
    public function getTranslationForeignKey()
132
    {
133
        if (isset($this->translationForeignKey)) {
134
            return $this->translationForeignKey;
135
        }
136
137
        return $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

137
        return $this->/** @scrutinizer ignore-call */ getForeignKey();
Loading history...
138
    }
139
140
    /**
141
     * Get the translatable.
142
     *
143
     * @return array
144
     * @throws \KoenHoeijmakers\LaravelTranslatable\Exceptions\MissingTranslationsException
145
     */
146
    public function getTranslatable(): array
147
    {
148
        if (!isset($this->translatable)) {
149
            throw new MissingTranslationsException('Model "' . static::class . '" is missing translations');
150
        }
151
152
        return $this->translatable;
153
    }
154
155
    /**
156
     * Get the translatable attributes.
157
     *
158
     * @return array
159
     */
160
    public function getTranslatableAttributes(): array
161
    {
162
        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

162
        return Arr::only($this->/** @scrutinizer ignore-call */ getAttributes(), $this->translatable);
Loading history...
163
    }
164
165
    /**
166
     * @param string $locale
167
     * @param array<string, mixed> $attributes
168
     * @return \Illuminate\Database\Eloquent\Model
169
     */
170
    public function storeTranslation(string $locale, array $attributes = [])
171
    {
172
        if (!is_null($model = $this->translations()->where($this->getLocaleKeyName(), $locale)->first())) {
173
            $model->update($attributes);
174
175
            return $model;
176
        }
177
178
        $model = $this->translations()->make($attributes);
179
        $model->setAttribute($this->getLocaleKeyName(), $locale);
180
        $model->save();
181
182
        return $model;
183
    }
184
185
    /**
186
     * Store many translations at once.
187
     *
188
     * @param array<string, array> $translations
189
     * @return $this
190
     */
191
    public function storeTranslations(array $translations)
192
    {
193
        foreach ($translations as $locale => $translation) {
194
            $this->storeTranslation($locale, $translation);
195
        }
196
197
        return $this;
198
    }
199
200
    /**
201
     * @param string $locale
202
     * @return \Illuminate\Database\Eloquent\Model|self
203
     */
204
    public function getTranslation(string $locale)
205
    {
206
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->first();
207
    }
208
209
    /**
210
     * The locale key name.
211
     *
212
     * @return string
213
     */
214
    public function getLocaleKeyName(): string
215
    {
216
        return $this->localeKeyName ?? config('translatable.locale_key_name', 'locale');
217
    }
218
219
    /**
220
     * Get the locale.
221
     *
222
     * @return string
223
     */
224
    public function getLocale(): string
225
    {
226
        return $this->currentLocale ?? 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

226
        return $this->currentLocale ?? app()->/** @scrutinizer ignore-call */ getLocale();
Loading history...
227
    }
228
229
    /**
230
     * Refresh the translation (in the current locale).
231
     *
232
     * @return \Illuminate\Database\Eloquent\Model|null
233
     */
234
    public function refreshTranslation()
235
    {
236
        if (!$this->exists) {
237
            return null;
238
        }
239
240
        $attributes = Arr::only(
241
            static::findOrFail($this->getKey())->attributes, $this->getTranslatable()
0 ignored issues
show
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

241
            static::findOrFail($this->/** @scrutinizer ignore-call */ getKey())->attributes, $this->getTranslatable()
Loading history...
242
        );
243
244
        foreach ($attributes as $key => $value) {
245
            $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

245
            $this->/** @scrutinizer ignore-call */ 
246
                   setAttribute($key, $value);
Loading history...
246
        }
247
248
        $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

248
        $this->/** @scrutinizer ignore-call */ 
249
               syncOriginal();
Loading history...
249
250
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type KoenHoeijmakers\LaravelT...latable\HasTranslations which is incompatible with the documented return type null|Illuminate\Database\Eloquent\Model.
Loading history...
251
    }
252
253
    /**
254
     * Translate the model to the given locale.
255
     *
256
     * @param string $locale
257
     * @return \Illuminate\Database\Eloquent\Model|null
258
     */
259
    public function translate(string $locale)
260
    {
261
        if (!$this->exists) {
262
            return null;
263
        }
264
265
        $this->currentLocale = $locale;
266
267
        return $this->refreshTranslation();
268
    }
269
270
    /**
271
     * Get a new query builder that doesn't have any global scopes (except the JoinTranslationScope).
272
     *
273
     * @return \Illuminate\Database\Eloquent\Builder
274
     */
275
    public function newQueryWithoutScopes(): Builder
276
    {
277
        return parent::newQueryWithoutScopes()
278
            ->withGlobalScope(JoinTranslationScope::class, new JoinTranslationScope());
279
    }
280
}
281