Completed
Branch master (e25020)
by Richan
03:06
created

TranslateableTrait::createTranslation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
c 0
b 0
f 0
rs 9.4285
cc 2
eloc 10
nc 2
nop 1
1
<?php
2
3
namespace RichanFongdasen\I18n\Eloquent\Extensions;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\Eloquent\Relations\HasMany;
8
use Illuminate\Support\Str;
9
use RichanFongdasen\I18n\Eloquent\Observer;
10
use RichanFongdasen\I18n\Eloquent\TranslationModel;
11
use RichanFongdasen\I18n\Eloquent\TranslationScope;
12
use RichanFongdasen\I18n\Locale;
13
14
trait TranslateableTrait
15
{
16
    /**
17
     * Fallback translation object. The model will use
18
     * the value from this object, if there was no
19
     * translated attribute value for current locale.
20
     *
21
     * @var \RichanFongdasen\I18n\Eloquent\TranslationModel
22
     */
23
    protected $fallbackTranslation;
24
25
    /**
26
     * Current selected locale.
27
     *
28
     * @var \RichanFongdasen\I18n\Locale
29
     */
30
    protected $locale;
31
32
    /**
33
     * Default language key.
34
     *
35
     * @var string
36
     */
37
    protected static $localeKey;
38
39
    /**
40
     * Translation object for the current selected
41
     * locale.
42
     *
43
     * @var \RichanFongdasen\I18n\Eloquent\TranslationModel
44
     */
45
    protected $translation;
46
47
    /**
48
     * Convert the model's attributes to an array.
49
     *
50
     * @return array
51
     */
52
    public function attributesToArray()
53
    {
54
        $attributes = parent::attributesToArray();
55
56
        foreach ($this->getTranslateableAttributes() as $key) {
57
            $attributes[$key] = $this->getAttribute($key);
58
        }
59
60
        return $attributes;
61
    }
62
63
    /**
64
     * Boot the TranslateableTrait model extension.
65
     *
66
     * @return void
67
     */
68
    public static function bootTranslateableTrait()
69
    {
70
        static::addGlobalScope(new TranslationScope());
71
        static::observe(app(Observer::class));
72
        static::$localeKey = \I18n::getConfig('language_key');
73
    }
74
75
    /**
76
     * Create a new translation for the given locale.
77
     *
78
     * @param \RichanFongdasen\I18n\Locale $locale
79
     *
80
     * @return \RichanFongdasen\I18n\Eloquent\TranslationModel
81
     */
82
    protected function createTranslation(Locale $locale)
83
    {
84
        $conditions = [
85
            $this->getForeignKey() => $this->getKey(),
86
            'locale'               => $locale->{self::$localeKey},
87
        ];
88
89
        $model = (new TranslationModel())
90
            ->fill($conditions);
91
92
        if (!$this->translations) {
93
            $this->translations = collect();
94
        }
95
96
        $this->translations->push($model);
97
98
        return $model;
99
    }
100
101
    /**
102
     * Fill the model with an array of attributes.
103
     *
104
     * @param array $attributes
105
     *
106
     * @return $this
107
     */
108
    public function fill(array $attributes)
109
    {
110
        foreach ($this->getTranslateableAttributes() as $key) {
111
            if (isset($attributes[$key])) {
112
                $this->setTranslateableAttribute($key, $attributes[$key]);
113
            }
114
        }
115
116
        return parent::fill($attributes);
117
    }
118
119
    /**
120
     * Get an attribute from the model.
121
     *
122
     * @param string $key
123
     *
124
     * @return mixed
125
     */
126
    public function getAttribute($key)
127
    {
128
        if ($this->isTranslateableAttribute($key)) {
129
            return $this->getTranslated($key);
130
        }
131
132
        return parent::getAttribute($key);
133
    }
134
135
    /**
136
     * Get join table attributes.
137
     *
138
     * @return string[]
139
     */
140
    protected function getJoinAttributes()
141
    {
142
        $attributes = [$this->getTable().'.*'];
143
144
        foreach ($this->getTranslateableAttributes() as $attribute) {
145
            $attributes[] = $this->getTranslationTable().'.'.$attribute;
146
        }
147
148
        return $attributes;
149
    }
150
151
    /**
152
     * Get all of translateable attributes.
153
     *
154
     * @return array
155
     */
156
    public function getTranslateableAttributes()
157
    {
158
        return is_array($this->translateFields) ? $this->translateFields : [];
159
    }
160
161
    /**
162
     * Get a translated attribute value from
163
     * the model.
164
     *
165
     * @param string $key
166
     *
167
     * @return mixed
168
     */
169
    protected function getTranslated($key)
170
    {
171
        if (!$this->locale) {
172
            $this->translate();
173
        }
174
175
        if ($result = $this->getTranslatedValue($this->translation, $key)) {
176
            return $result;
177
        }
178
179
        return $this->getTranslatedValue($this->fallbackTranslation, $key);
180
    }
181
182
    /**
183
     * Get a translated attribute value from
184
     * the given translation model.
185
     *
186
     * @param mixed  $translation
187
     * @param string $key
188
     *
189
     * @return mixed
190
     */
191
    protected function getTranslatedValue($translation, $key)
192
    {
193
        if (!$translation instanceof Model) {
194
            return null;
195
        }
196
197
        return $translation->getAttribute($key);
198
    }
199
200
    /**
201
     * Get existing translation or create a
202
     * new one.
203
     *
204
     * @param \RichanFongdasen\I18n\Locale $locale
205
     *
206
     * @return \RichanFongdasen\I18n\Eloquent\TranslationModel
207
     */
208
    protected function getTranslation(Locale $locale)
209
    {
210
        $this->translate($locale);
211
212
        if ($this->translation) {
213
            return $this->translation;
214
        }
215
216
        return $this->translation = $this->createTranslation($locale);
217
    }
218
219
    /**
220
     * Find locale object based on the given
221
     * key value.
222
     *
223
     * @param mixed $key
224
     *
225
     * @return \RichanFongdasen\I18n\Locale
226
     */
227
    protected function getTranslationLocale($key = null)
228
    {
229
        if ($key instanceof Locale) {
230
            return $key;
231
        }
232
233
        if (empty($key)) {
234
            $key = \App::getLocale();
235
        }
236
237
        if (!$locale = \I18n::getLocale($key)) {
238
            $locale = \I18n::defaultLocale();
239
        }
240
241
        return $locale;
242
    }
243
244
    /**
245
     * Get translation table.
246
     *
247
     * @return string
248
     */
249
    public function getTranslationTable()
250
    {
251
        if (!isset($this->translationTable)) {
252
            $suffix = \I18n::getConfig('translation_table_suffix');
253
254
            return Str::snake(class_basename($this)).'_'.$suffix;
255
        }
256
257
        return $this->translationTable;
258
    }
259
260
    /**
261
     * Check whether the given attribute key is
262
     * translateable.
263
     *
264
     * @param string $key
265
     *
266
     * @return bool
267
     */
268
    protected function isTranslateableAttribute($key)
269
    {
270
        $fields = $this->getTranslateableAttributes();
271
272
        return in_array($key, $fields);
273
    }
274
275
    /**
276
     * Add and additional scope to join the translation table
277
     * and make the translation content more easier to search.
278
     *
279
     * @param \Illuminate\Database\Eloquent\Builder $query
280
     *
281
     * @return \Illuminate\Database\Eloquent\Builder
282
     */
283
    public function scopeJoinTranslation(Builder $query)
284
    {
285
        $attributes = $this->getJoinAttributes();
286
287
        return $query->leftJoin(
288
            $this->getTranslationTable(),
289
            $this->getTable().'.'.$this->getKeyName(),
290
            '=',
291
            $this->getTranslationTable().'.'.$this->getForeignKey()
292
        )->select($attributes)
293
        ->where($this->getTranslationTable().'.locale', \App::getLocale());
294
    }
295
296
    /**
297
     * Set a given attribute on the model.
298
     *
299
     * @param string $key
300
     * @param mixed  $value
301
     *
302
     * @return $this
303
     */
304
    public function setAttribute($key, $value)
305
    {
306
        if ($this->isTranslateableAttribute($key)) {
307
            if (!$this->locale) {
308
                $this->translate();
309
            }
310
            if (!$this->translation instanceof TranslationModel) {
311
                $this->translation = $this->getTranslation($this->locale);
312
            }
313
314
            $this->translation->setAttribute($key, $value);
315
            $this->updateTimestamps();
316
317
            return $this;
318
        }
319
320
        return parent::setAttribute($key, $value);
321
    }
322
323
    /**
324
     * Set fallback translation model.
325
     *
326
     * @return void
327
     */
328
    protected function setFallbackTranslation()
329
    {
330
        $locale = \I18n::defaultLocale()->{self::$localeKey};
331
        $this->fallbackTranslation = $this->translations->where('locale', $locale)->first();
332
    }
333
334
    /**
335
     * Set translateable attribute based on the
336
     * given key.
337
     *
338
     * @param string $key
339
     * @param mixed  $data
340
     * @param mixed  $locale
341
     *
342
     * @return $this
343
     */
344
    protected function setTranslateableAttribute($key, $data, $locale = null)
345
    {
346
        if (is_array($data)) {
347
            foreach ($data as $language => $value) {
348
                $this->setTranslateableAttribute($key, $value, $language);
349
            }
350
351
            return $this;
352
        }
353
        if (!$locale && $this->locale) {
354
            $locale = $this->locale;
355
        }
356
        $this->translate($locale);
357
        $this->setAttribute($key, $data);
358
359
        return $this;
360
    }
361
362
    /**
363
     * Translate current model.
364
     *
365
     * @param mixed $key
366
     *
367
     * @return $this
368
     */
369
    public function translate($key = null)
370
    {
371
        if (!$this->fallbackTranslation) {
372
            $this->setFallbackTranslation();
373
        }
374
375
        $this->locale = $this->getTranslationLocale($key);
376
377
        $key = $this->locale->{self::$localeKey};
378
        $this->translation = $this->translations->where('locale', $key)->first();
379
380
        return $this;
381
    }
382
383
    /**
384
     * Define HasMany model relationship
385
     * with its translation model.
386
     *
387
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
388
     */
389
    public function translations()
390
    {
391
        $model = new TranslationModel();
392
        $model->setTable($this->getTranslationTable());
393
394
        return new HasMany(
395
            $model->newQuery(),
396
            $this,
397
            $this->getForeignKey(),
398
            $this->getKeyName()
399
        );
400
    }
401
402
    /**
403
     * Get the default foreign key name for the model.
404
     *
405
     * @return string
406
     */
407
    abstract public function getForeignKey();
408
409
    /**
410
     * Get the value of the model's primary key.
411
     *
412
     * @return mixed
413
     */
414
    abstract public function getKey();
415
416
    /**
417
     * Get the primary key for the model.
418
     *
419
     * @return string
420
     */
421
    abstract public function getKeyName();
422
423
    /**
424
     * Get the table associated with the model.
425
     *
426
     * @return string
427
     */
428
    abstract public function getTable();
429
430
    /**
431
     * Update the creation and update timestamps.
432
     *
433
     * @return void
434
     */
435
    abstract protected function updateTimestamps();
436
}
437