Passed
Push — master ( af745e...ec06c1 )
by Koen
02:49
created

formatTranslatableColumnsForSelect()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 7
rs 9.4285
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
     * Purge the translations.
72
     *
73
     * @return mixed
74
     */
75
    public function purgeTranslations()
76
    {
77
        return $this->translations()->delete();
78
    }
79
80
    /**
81
     * Get the translation model.
82
     *
83
     * @return string
84
     */
85
    public function getTranslationModel(): string
86
    {
87
        return property_exists($this, 'translationModel')
88
            ? $this->translationModel
89
            : get_class($this) . $this->getTranslationModelSuffix();
90
    }
91
92
    /**
93
     * Get the translation model suffix.
94
     *
95
     * @return string
96
     */
97
    protected function getTranslationModelSuffix(): string
98
    {
99
        return 'Translation';
100
    }
101
102
    /**
103
     * Get the translation table.
104
     *
105
     * @return string
106
     */
107
    public function getTranslationTable(): string
108
    {
109
        $model = $this->getTranslationModel();
110
111
        return (new $model())->getTable();
112
    }
113
114
    /**
115
     * Get the translation foreign key.
116
     *
117
     * @return string
118
     */
119
    public function getTranslationForeignKey()
120
    {
121
        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

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

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

211
        return property_exists($this, 'currentLocale') ? $this->currentLocale : app()->/** @scrutinizer ignore-call */ getLocale();
Loading history...
Bug Best Practice introduced by
The expression return property_exists($...le : app()->getLocale() could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
212
    }
213
214
    /**
215
     * Refresh the translation (in the current locale).
216
     *
217
     * @return \Illuminate\Database\Eloquent\Model|null
218
     */
219
    public function refreshTranslation()
220
    {
221
        if (!$this->exists) {
222
            return null;
223
        }
224
225
        $attributes = Arr::only(
226
            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

226
            static::findOrFail($this->/** @scrutinizer ignore-call */ getKey())->attributes, $this->getTranslatable()
Loading history...
227
        );
228
229
        foreach ($attributes as $key => $value) {
230
            $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

230
            $this->/** @scrutinizer ignore-call */ 
231
                   setAttribute($key, $value);
Loading history...
231
        }
232
233
        $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

233
        $this->/** @scrutinizer ignore-call */ 
234
               syncOriginal();
Loading history...
234
235
        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...
236
    }
237
238
    /**
239
     * Translate the model to the given locale.
240
     *
241
     * @param string $locale
242
     * @return \Illuminate\Database\Eloquent\Model|null
243
     */
244
    public function translate(string $locale)
245
    {
246
        if (!$this->exists) {
247
            return null;
248
        }
249
250
        $this->currentLocale = $locale;
251
252
        return $this->refreshTranslation();
253
    }
254
255
    /**
256
     * Format the translated columns.
257
     *
258
     * @return array
259
     */
260
    public function formatTranslatableColumnsForSelect(): array
261
    {
262
        $table = $this->getTranslationTable();
263
264
        return array_map(function ($item) use ($table) {
265
            return $table . '.' . $item;
266
        }, $this->getTranslatable());
267
    }
268
269
    /**
270
     * Get a new query builder that doesn't have any global scopes (except the JoinTranslationScope).
271
     *
272
     * @return \Illuminate\Database\Eloquent\Builder
273
     */
274
    public function newQueryWithoutScopes(): Builder
275
    {
276
        return parent::newQueryWithoutScopes()
277
            ->withGlobalScope(JoinTranslationScope::class, new JoinTranslationScope());
278
    }
279
280
    /**
281
     * Retrieve the model for a bound value.
282
     *
283
     * @param  mixed  $value
284
     * @return \Illuminate\Database\Eloquent\Model|null
285
     */
286
    public function resolveRouteBinding($value)
287
    {
288
        return $this->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

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

288
        return $this->/** @scrutinizer ignore-call */ where($this->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

288
        return $this->where($this->getTable() . '.' . $this->/** @scrutinizer ignore-call */ getRouteKeyName(), $value)->first();
Loading history...
289
    }
290
}
291