Issues (1)

src/TranslatableBehavior.php (1 issue)

Labels
Severity
1
<?php
2
/**
3
 * @link https://github.com/yiimaker/yii2-translatable
4
 * @copyright Copyright (c) 2017-2018 Yii Maker
5
 * @license BSD 3-Clause License
6
 */
7
8
namespace ymaker\translatable;
9
10
use Yii;
11
use yii\base\Behavior;
12
use yii\base\Model;
13
use yii\db\BaseActiveRecord;
14
15
/**
16
 * Behavior aggregates logic of linking translations to the primary model.
17
 *
18
 * @author Vladimir Kuprienko <[email protected]>
19
 * @since 1.0
20
 */
21
class TranslatableBehavior extends Behavior
22
{
23
    /**
24
     * The owner of this behavior.
25
     *
26
     * @var BaseActiveRecord
27
     */
28
    public $owner;
29
30
    /**
31
     * Name of translation relation in main model.
32
     *
33
     * @var string
34
     */
35
    public $translationRelationName = 'translations';
36
37
    /**
38
     * Name of attribute in translation model that contains language.
39
     *
40
     * @var string
41
     */
42
    public $translationLanguageAttrName = 'language';
43
44
    /**
45
     * List of attribute names from translation model that should be translated.
46
     *
47
     * @var string[]
48
     */
49
    public $translationAttributeList;
50
51
    /**
52
     * Patter for the attribute name in validation errors list.
53
     *
54
     * @var string
55
     */
56
    public $attributeNamePattern = '%name% [%language%]';
57
58
    /**
59
     * Temp storage for translation entity objects.
60
     *
61
     * @var array
62
     */
63
    protected $translationsBuffer = [];
64
65
66
    /**
67
     * Translate model to needed language.
68
     *
69
     * Alias for @see getTranslation() method.
70
     *
71
     * @param null|string $language
72
     *
73
     * @return \yii\db\ActiveRecord|BaseActiveRecord
74
     */
75
    final public function translateTo($language = null)
76
    {
77
        return $this->getTranslation($language);
78
    }
79
80
    /**
81
     * Returns translation entity object for needed language.
82
     *
83
     * @param null|string $language By default uses application current language.
84
     *
85
     * @return \yii\db\ActiveRecord|BaseActiveRecord
86
     */
87
    public function getTranslation($language = null)
88
    {
89
        $language = $language ?: Yii::$app->language;
90
        $translations = $this->getModelTranslations();
91
92
        // search translation by language in exists translations
93
        foreach ($translations as $translation) {
94
            // if translation exists - return it
95
            if ($translation->getAttribute($this->translationLanguageAttrName) === $language) {
96
                $this->translationsBuffer[] = $translation;
97
98
                return $translation;
99
            }
100
        }
101
102
        // if translation doesn't exist - create and return
103
        $translationEntityClass = $this->owner->getRelation($this->translationRelationName)->modelClass;
0 ignored issues
show
Accessing modelClass on the interface yii\db\ActiveQueryInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
104
105
        /* @var BaseActiveRecord $translation */
106
        $translation = new $translationEntityClass();
107
        $translation->setAttribute($this->translationLanguageAttrName, $language);
108
        $translations[] = $translation;
109
        $this->translationsBuffer = $translations;
110
        $this->owner->populateRelation($this->translationRelationName, $translations);
111
112
        return $translation;
113
    }
114
115
    /**
116
     * Check whether translation exists.
117
     *
118
     * @param null|string $language By default uses application current language.
119
     *
120
     * @return bool
121
     */
122
    public function hasTranslation($language = null)
123
    {
124
        $language = $language ?: Yii::$app->language;
125
126
        foreach ($this->getModelTranslations() as $translation) {
127
            if ($translation->getAttribute($this->translationLanguageAttrName) === $language) {
128
                return true;
129
            }
130
        }
131
132
        return false;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function events()
139
    {
140
        return [
141
            BaseActiveRecord::EVENT_AFTER_VALIDATE => 'afterValidate',
142
            BaseActiveRecord::EVENT_AFTER_INSERT => 'afterSave',
143
            BaseActiveRecord::EVENT_AFTER_UPDATE => 'afterSave',
144
        ];
145
    }
146
147
    /**
148
     * Triggers after validation of the main model.
149
     */
150
    public function afterValidate()
151
    {
152
        $translations = $this->getModelTranslations();
153
154
        $isValid = Model::validateMultiple($translations, $this->translationAttributeList);
155
156
        if (!$isValid) {
157
            foreach ($translations as $translation) {
158
                foreach ($translation->getErrors() as $attribute => $errors) {
159
                    $attribute = \strtr($this->attributeNamePattern, [
160
                        '%name%' => $attribute,
161
                        '%language%' => $translation->{$this->translationLanguageAttrName},
162
                    ]);
163
164
                    if (\is_array($errors)) {
165
                        foreach ($errors as $error) {
166
                            $this->owner->addError($attribute, $error);
167
                        }
168
                    } else {
169
                        $this->owner->addError($attribute, $errors);
170
                    }
171
                }
172
            }
173
        }
174
    }
175
    
176
    /**
177
     * Triggers after saving of the main model.
178
     */
179
    public function afterSave()
180
    {
181
        foreach ($this->translationsBuffer as $translation) {
182
            $this->owner->link($this->translationRelationName, $translation);
183
        }
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    public function canGetProperty($name, $checkVars = true)
190
    {
191
        return \in_array($name, $this->translationAttributeList);
192
    }
193
    
194
    /**
195
     * {@inheritdoc}
196
     */
197
    public function canSetProperty($name, $checkVars = true)
198
    {
199
        return \in_array($name, $this->translationAttributeList);
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function __get($name)
206
    {
207
        $translation = $this->getTranslation();
208
209
        if ($translation->hasAttribute($name)) {
210
            return $translation->getAttribute($name);
211
        }
212
213
        return $this->owner->$name;
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219
    public function __set($name, $value)
220
    {
221
        $translation = $this->getTranslation();
222
223
        if ($translation->hasAttribute($name)) {
224
            $translation->setAttribute($name, $value);
225
        }
226
227
        $this->owner->$name = $value;
228
    }
229
230
    /**
231
     * @return \yii\db\ActiveRecord[]
232
     */
233
    protected function getModelTranslations()
234
    {
235
        return $this->owner->{$this->translationRelationName};
236
    }
237
}
238