MultilingualModelTrait::isMultiLanguageField()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 9
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 18
rs 9.9666
1
<?php
2
namespace Itstructure\Mult\Traits;
3
4
use Illuminate\Database\Eloquent\Model;
5
use Illuminate\Database\Eloquent\Relations\{HasMany, HasOne};
6
use Illuminate\Database\Eloquent\Collection;
7
use Itstructure\Mult\Classes\MultilingualMigration;
8
use Itstructure\Mult\Models\Language;
9
10
/**
11
 * Class MultilingualModelTrait
12
 *
13
 * @property Collection|Model[] $translateList
14
 *
15
 * @method HasMany hasMany($related, $foreignKey = null, $localKey = null)
16
 * @method HasOne hasOne($related, $foreignKey = null, $localKey = null)
17
 * @method bool isFillable($key)
18
 *
19
 * @package Itstructure\Mult\Traits
20
 *
21
 * @author Andrey Girnik <[email protected]>
22
 */
23
trait MultilingualModelTrait
24
{
25
    /**
26
     * Container for temporary storage of translation data.
27
     * @var array
28
     */
29
    protected $tmpTranslateStorage = [];
30
31
    /**
32
     * @var null|HasMany
33
     */
34
    protected $tmpTranslateList = null;
35
36
    /**
37
     * Return related translate model name.
38
     * @return string
39
     */
40
    public static function translateModelClass()
41
    {
42
        $primaryClass = new \ReflectionClass(static::class);
43
        return $primaryClass->getNamespaceName() . '\\' . $primaryClass->getShortName() . 'Language';
44
    }
45
46
    /**
47
     * Override model magic getter. Return translate for field.
48
     * Example: if we try $model->title_en, we will get 'title' in english.
49
     * @param string $name field name.
50
     * @return mixed|null
51
     */
52
    public function __get($name)
53
    {
54
        if (false === $this->isMultiLanguageField($name)) {
55
            return parent::__get($name);
56
        }
57
58
        $nameArray = explode('_', $name);
59
        $lang = array_pop($nameArray);
60
        $field = implode('_', $nameArray);
61
62
        foreach ($this->translateList()->get() as $translate) {
63
            if ($translate->language->short_name === $lang) {
64
                return $translate->{$field};
65
            }
66
        }
67
68
        return null;
69
    }
70
71
    /**
72
     * Override model magic setter. Set translation for the field.
73
     * For example $model->title_en  will save title field in translate model where
74
     * languages_id => record in language with 'en' locale.
75
     * @param string $name  name of field.
76
     * @param mixed  $value value to be stored in field.
77
     * @return void
78
     */
79
    public function __set($name, $value)
80
    {
81
        if (false === $this->isMultiLanguageField($name)) {
82
            if ($this->isFillable($name)) {
83
                parent::__set($name, $value);
84
            }
85
            return;
86
        }
87
88
        $nameArray = explode('_', $name);
89
        $lang = array_pop($nameArray);
90
        $field = implode('_', $nameArray);
91
92
        $this->tmpTranslateStorage[$lang][$field] = $value;
93
    }
94
95
    /**
96
     * Return key name of relation between main table and translations table.
97
     * @return string
98
     */
99
    public function keyToMainModel()
100
    {
101
        /* @var Model $this */
102
        return $this->table . '_id';
103
    }
104
105
    /**
106
     * Return translations table name.
107
     * @return string
108
     */
109
    public function translateTableName()
110
    {
111
        /* @var Model $this */
112
        return $this->table . '_' . MultilingualMigration::LANGUAGE_TABLE_NAME;
113
    }
114
115
    /**
116
     * Return related translated queries.
117
     * @return HasMany
118
     */
119
    public function translateList()
120
    {
121
        if ($this->tmpTranslateList == null) {
122
            $this->tmpTranslateList = $this->hasMany(static::translateModelClass(), $this->keyToMainModel(), MultilingualMigration::PRIMARY_KEY_NAME);
123
        }
124
125
        return $this->tmpTranslateList;
126
    }
127
128
    /**
129
     * Returns default translate. If field name is given, we can take an alternative
130
     * translate when default translate value is empty.
131
     * @param string|null $field
132
     * @param string $ifNotExistsValue
133
     * @return string
134
     */
135
    public function defaultTranslate(string $field = null, string $ifNotExistsValue = '-')
136
    {
137
        /* @var Model $this */
138
        $mainRequest = $this->hasOne(static::translateModelClass(), $this->keyToMainModel(), MultilingualMigration::PRIMARY_KEY_NAME);
0 ignored issues
show
Bug introduced by
It seems like $this->keyToMainModel() can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $foreignKey of Illuminate\Database\Eloquent\Model::hasOne() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

138
        $mainRequest = $this->hasOne(static::translateModelClass(), /** @scrutinizer ignore-type */ $this->keyToMainModel(), MultilingualMigration::PRIMARY_KEY_NAME);
Loading history...
Bug introduced by
It seems like static::translateModelClass() can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $related of Illuminate\Database\Eloquent\Model::hasOne() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

138
        $mainRequest = $this->hasOne(/** @scrutinizer ignore-type */ static::translateModelClass(), $this->keyToMainModel(), MultilingualMigration::PRIMARY_KEY_NAME);
Loading history...
139
140
        $defaultTranslate = $mainRequest->where([
141
            MultilingualMigration::keyToLanguageTable() => Language::firstWhere('default', 1)->{MultilingualMigration::PRIMARY_KEY_NAME}
142
        ]);
143
144
        if ($field !== null && $defaultTranslate->where($field, '!=', '')->whereNotNull($field)->count() == 0) {
145
            $result = $mainRequest->where($field, '!=', '')->whereNotNull($field)->first();
146
147
            return empty($result) ? $ifNotExistsValue : $result->{$field};
148
        }
149
150
        return $field === null ? $defaultTranslate->first() : $defaultTranslate->first()->{$field};
151
    }
152
153
    /**
154
     * Override model method to save all translations after main model saved.
155
     * @return void
156
     */
157
    protected static function booted()
158
    {
159
        static::saved(function($model) {
160
            /* @var static $model */
161
            foreach ($model->tmpTranslateStorage as $lang => $fields) {
162
                $langModel = Language::firstWhere('short_name', $lang);
163
164
                $translateModelQuery = forward_static_call_array([static::translateModelClass(), 'where'], [$model->keyToMainModel(), $model->{MultilingualMigration::PRIMARY_KEY_NAME}]);
165
                $translateModelQuery->where(MultilingualMigration::keyToLanguageTable(), $langModel->{MultilingualMigration::PRIMARY_KEY_NAME});
166
                $updated = $translateModelQuery->update($fields);
167
168
                if (!$updated) {
169
                    forward_static_call_array([static::translateModelClass(), 'create'], [
170
                        array_merge($fields, [
171
                            $model->keyToMainModel() => $model->{MultilingualMigration::PRIMARY_KEY_NAME},
172
                            MultilingualMigration::keyToLanguageTable() => $langModel->{MultilingualMigration::PRIMARY_KEY_NAME}
173
                        ])
174
                    ]);
175
                }
176
            }
177
        });
178
    }
179
180
    /**
181
     * Check for multi-language mode of field.
182
     * @param string $name name of field to be checked.
183
     * @return boolean
184
     */
185
    protected function isMultiLanguageField($name): bool
186
    {
187
        if (false === strpos($name, '_')) {
188
            return false;
189
        }
190
191
        $nameArray = explode('_', $name);
192
        $lang = array_pop($nameArray);
193
194
        if (null === $lang) {
195
            return false;
196
        }
197
198
        if (false === in_array($lang, Language::shortLanguageList(), true)) {
199
            return false;
200
        }
201
202
        return true;
203
    }
204
}
205