Issues (22)

src/HasTranslations.php (1 issue)

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 34
    public static function bootHasTranslations()
32
    {
33 34
        if (config('translatable.use_saving_service', true)) {
34
            self::saving(function ($model) {
35 24
                app(TranslationSavingService::class)->rememberTranslationForModel($model);
36 34
            });
37
38
            self::saved(function ($model) {
39 24
                app(TranslationSavingService::class)->storeTranslationOnModel($model);
40
41 24
                $model->refreshTranslation();
42 34
            });
43
        }
44
45
        self::deleting(function ($model) {
46 2
            $model->purgeTranslations();
47 34
        });
48
49 34
        self::addGlobalScope(new JoinTranslationScope());
50 34
    }
51
52
    /**
53
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
54
     */
55 24
    public function translations(): HasMany
56
    {
57 24
        return $this->hasMany($this->getTranslationModel(), $this->getTranslationForeignKey());
58
    }
59
60
    /**
61
     * Check if the translation by the given locale exists.
62
     *
63
     * @param  string $locale
64
     * @return bool
65
     */
66 12
    public function translationExists(string $locale): bool
67
    {
68 12
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->exists();
69
    }
70
71
    /**
72
     * Purge the translations.
73
     *
74
     * @return mixed
75
     */
76 4
    public function purgeTranslations()
77
    {
78 4
        return $this->translations()->delete();
79
    }
80
81
    /**
82
     * Get the translation model.
83
     *
84
     * @return string
85
     */
86 26
    public function getTranslationModel(): string
87
    {
88 26
        return property_exists($this, 'translationModel')
89 2
            ? $this->translationModel
90 26
            : get_class($this) . $this->getTranslationModelSuffix();
91
    }
92
93
    /**
94
     * Get the translation model suffix.
95
     *
96
     * @return string
97
     */
98 24
    protected function getTranslationModelSuffix(): string
99
    {
100 24
        return 'Translation';
101
    }
102
103
    /**
104
     * Get the translation table.
105
     *
106
     * @return string
107
     */
108 26
    public function getTranslationTable(): string
109
    {
110 26
        $model = $this->getTranslationModel();
111
112 26
        return (new $model())->getTable();
113
    }
114
115
    /**
116
     * Get the translation foreign key.
117
     *
118
     * @return string
119
     */
120 24
    public function getTranslationForeignKey()
121
    {
122 24
        return property_exists($this, 'translationForeignKey') ? $this->translationForeignKey : $this->getForeignKey();
123
    }
124
125
    /**
126
     * Get the translatable.
127
     *
128
     * @return array
129
     * @throws \KoenHoeijmakers\LaravelTranslatable\Exceptions\MissingTranslationsException
130
     */
131 26
    public function getTranslatable(): array
132
    {
133 26
        if (! isset($this->translatable)) {
134 2
            throw new MissingTranslationsException('Model "' . get_class($this) . '" is missing translations');
135
        }
136
137 24
        return $this->translatable;
138
    }
139
140
    /**
141
     * Get the translatable attributes.
142
     *
143
     * @return array
144
     */
145 24
    public function getTranslatableAttributes(): array
146
    {
147 24
        return Arr::only($this->getAttributes(), $this->translatable);
148
    }
149
150
    /**
151
     * @param  string               $locale
152
     * @param  array<string, mixed> $attributes
153
     * @return \Illuminate\Database\Eloquent\Model
154
     */
155 24
    public function storeTranslation(string $locale, array $attributes = [])
156
    {
157 24
        if (! is_null($model = $this->translations()->where($this->getLocaleKeyName(), $locale)->first())) {
158 2
            $model->update($attributes);
159
160 2
            return $model;
161
        }
162
163 24
        $model = $this->translations()->make($attributes);
164 24
        $model->setAttribute($this->getLocaleKeyName(), $locale);
165 24
        $model->save();
166
167 24
        return $model;
168
    }
169
170
    /**
171
     * Store many translations at once.
172
     *
173
     * @param  array<string, array> $translations
174
     * @return $this
175
     */
176 2
    public function storeTranslations(array $translations)
177
    {
178 2
        foreach ($translations as $locale => $translation) {
179 2
            $this->storeTranslation($locale, $translation);
180
        }
181
182 2
        return $this;
183
    }
184
185
    /**
186
     * @param  string $locale
187
     * @return \Illuminate\Database\Eloquent\Model|self
188
     */
189 4
    public function getTranslation(string $locale)
190
    {
191 4
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->first();
192
    }
193
194
    /**
195
     * @param  string $locale
196
     * @param  string $name
197
     * @return mixed
198
     */
199 2
    public function getTranslationValue(string $locale, string $name)
200
    {
201 2
        return $this->translations()->where($this->getLocaleKeyName(), $locale)->value($name);
202
    }
203
204
    /**
205
     * The locale key name.
206
     *
207
     * @return string
208
     */
209 26
    public function getLocaleKeyName(): string
210
    {
211 26
        return property_exists($this, 'localeKeyName')
212 2
            ? $this->localeKeyName
213 26
            : config()->get('translatable.locale_key_name', 'locale');
214
    }
215
216
    /**
217
     * Get the locale.
218
     *
219
     * @return string
220
     */
221 24
    public function getLocale(): string
222
    {
223 24
        return null !== $this->currentLocale
224 4
            ? $this->currentLocale
225 24
            : app()->getLocale();
226
    }
227
228
    /**
229
     * Refresh the translation (in the current locale).
230
     *
231
     * @return \Illuminate\Database\Eloquent\Model|null|HasTranslations
232
     * @throws \KoenHoeijmakers\LaravelTranslatable\Exceptions\MissingTranslationsException
233
     */
234 26
    public function refreshTranslation()
235
    {
236 26
        if (! $this->exists) {
237 2
            return null;
238
        }
239
240 24
        $attributes = Arr::only(
241 24
            $this->newQuery()->findOrFail($this->getKey())->attributes, $this->getTranslatable()
242
        );
243
244 24
        foreach ($attributes as $key => $value) {
245 24
            $this->setAttribute($key, $value);
246
        }
247
248 24
        $this->syncOriginal();
249
250 24
        return $this;
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 6
    public function translate(string $locale)
260
    {
261 6
        if (! $this->exists) {
262 2
            return null;
263
        }
264
265 4
        $this->currentLocale = $locale;
266
267 4
        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...
268
    }
269
270
    /**
271
     * Format the translated columns.
272
     *
273
     * @return array
274
     * @throws \KoenHoeijmakers\LaravelTranslatable\Exceptions\MissingTranslationsException
275
     */
276 24
    public function formatTranslatableColumnsForSelect(): array
277
    {
278 24
        $table = $this->getTranslationTable();
279
280
        return array_map(function ($item) use ($table) {
281 24
            return $table . '.' . $item;
282 24
        }, $this->getTranslatable());
283
    }
284
285
    /**
286
     * Get a new query builder that doesn't have any global scopes (except the JoinTranslationScope).
287
     *
288
     * @return \Illuminate\Database\Eloquent\Builder
289
     */
290 24
    public function newQueryWithoutScopes(): Builder
291
    {
292 24
        return parent::newQueryWithoutScopes()
293 24
            ->withGlobalScope(JoinTranslationScope::class, new JoinTranslationScope());
294
    }
295
296
    /**
297
     * Retrieve the model for a bound value.
298
     *
299
     * @param  mixed $value
300
     * @param  null  $field
301
     * @return \Illuminate\Database\Eloquent\Model|null
302
     */
303 2
    public function resolveRouteBinding($value, $field = null)
304
    {
305 2
        $field = $field ?? $this->getRouteKeyName();
306
307 2
        return $this->newQuery()->where($this->getTable() . '.' . $field, $value)->first();
308
    }
309
}
310