Issues (35)

src/models/MultilanguageValidateModel.php (8 issues)

1
<?php
2
3
namespace Itstructure\AdminModule\models;
4
5
use yii\db\ActiveRecord as BaseActiveRecord;
6
use yii\base\Model;
7
use yii\helpers\ArrayHelper;
8
use Itstructure\AdminModule\interfaces\ModelInterface;
9
10
/**
11
 * Class MultilanguageValidateModel
12
 * General validation model together with multilingual fields.
13
 *
14
 * @property array $dynamicFields Dynamic fields from which the translated fields are formed.
15
 * @property BaseActiveRecord|MultilanguageTrait $mainModel Basic data model.
16
 *
17
 * @package Itstructure\AdminModule\models
18
 *
19
 * @author Andrey Girnik <[email protected]>
20
 */
21
class MultilanguageValidateModel extends Model implements ModelInterface
22
{
23
    /**
24
     * Dynamic fields from which the translated fields are formed.
25
     *
26
     * @var array
27
     */
28
    public $dynamicFields = [];
29
30
    /**
31
     * Basic data model.
32
     *
33
     * @var BaseActiveRecord|MultilanguageTrait
34
     */
35
    private $mainModel;
36
37
    /**
38
     * Validation rules for all fields together with dynamic.
39
     *
40
     * @return array
41
     */
42
    public function rules(): array
43
    {
44
        return ArrayHelper::merge(
45
            $this->getDynamicValidationRules(),
46
            $this->mainModel->rules()
0 ignored issues
show
It seems like rules() 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

46
            $this->mainModel->/** @scrutinizer ignore-call */ 
47
                              rules()
Loading history...
47
        );
48
    }
49
50
    /**
51
     * Scenarios.
52
     *
53
     * @return array
54
     */
55
    public function scenarios(): array
56
    {
57
        return [
58
            ModelInterface::SCENARIO_CREATE => $this->attributes(),
59
            ModelInterface::SCENARIO_UPDATE => $this->attributes(),
60
            self::SCENARIO_DEFAULT => $this->attributes(),
61
        ];
62
    }
63
64
    /**
65
     * Labels of all fields.
66
     *
67
     * @inheritdoc
68
     */
69
    public function attributeLabels()
70
    {
71
        $dynamicAttributeLabels = [];
72
        $staticAttributeLabels = [];
73
        $translateAttributeLabels = [];
74
75
        $translateModelName = $this->mainModel->getTranslateModelName();
76
77
        if (method_exists($translateModelName, 'attributeLabels')) {
78
            /** @var Model $abstractTranslateModel */
79
            $abstractTranslateModel = new $translateModelName();
80
            $translateAttributeLabels = $abstractTranslateModel->attributeLabels();
81
        }
82
83
        foreach ($this->dynamicFields as $fieldConditions) {
84
85
            $fieldName = $fieldConditions['name'];
86
87
            if (array_key_exists($fieldName, $translateAttributeLabels)) {
88
89
                $staticAttributeLabels[$fieldName] = $translateAttributeLabels[$fieldName];
90
91
                foreach ($this->getShortLanguageList() as $language) {
92
                    $dynamicAttributeLabels[$fieldName.'_'.$language] = $translateAttributeLabels[$fieldName];
93
                }
94
            }
95
        }
96
97
        return ArrayHelper::merge(
98
            ArrayHelper::merge(
99
                $dynamicAttributeLabels,
100
                $staticAttributeLabels
101
            ),
102
            $this->mainModel->attributeLabels()
0 ignored issues
show
It seems like attributeLabels() 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

102
            $this->mainModel->/** @scrutinizer ignore-call */ 
103
                              attributeLabels()
Loading history...
103
        );
104
    }
105
106
    /**
107
     * Specifies the value of the field.
108
     *
109
     * @param string $name - name of field.
110
     * @param mixed  $value - value to be stored in field.
111
     *
112
     * @return void
113
     */
114
    public function __set($name, $value)
115
    {
116
        $setter = 'set' . $name;
117
        if (method_exists($this, $setter)) {
118
            $this->$setter($value);
119
        } else {
120
            $this->{$name} = $value;
121
        }
122
    }
123
124
    /**
125
     * Gets the value of the field.
126
     *
127
     * @param string $name - field name.
128
     *
129
     * @return mixed
130
     */
131
    public function __get($name)
132
    {
133
        $getter = 'get' . $name;
134
        if (method_exists($this, $getter)) {
135
            return $this->$getter();
136
        }
137
138
        if ($this->mainModel->isNewRecord) {
0 ignored issues
show
Bug Best Practice introduced by
The property isNewRecord does not exist on Itstructure\AdminModule\models\MultilanguageTrait. Since you implemented __get, consider adding a @property annotation.
Loading history...
139
            return $this->{$name} ?? '';
140
        } else {
141
            return $this->mainModel->{$name} ?? '';
142
        }
143
    }
144
145
    /**
146
     * Setter for main model.
147
     *
148
     * @param BaseActiveRecord $mainModel
149
     */
150
    public function setMainModel(BaseActiveRecord $mainModel)
151
    {
152
        $this->mainModel = $mainModel;
153
    }
154
155
    /**
156
     * Getter for main model.
157
     *
158
     * @return mixed
159
     */
160
    public function getMainModel()
161
    {
162
        return $this->mainModel;
163
    }
164
165
    /**
166
     * Attributes along with dynamic and from the basic model.
167
     *
168
     * @return array
169
     */
170
    public function attributes(): array
171
    {
172
        if (method_exists($this->mainModel, 'mainModelAttributes')) {
173
            $mainModelAttributes = call_user_func([
174
                $this->mainModel,
175
                'mainModelAttributes'
176
            ]);
177
        } else {
178
            $mainModelAttributes = $this->mainModel->attributes();
0 ignored issues
show
It seems like attributes() 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

178
            /** @scrutinizer ignore-call */ 
179
            $mainModelAttributes = $this->mainModel->attributes();
Loading history...
179
        }
180
181
        return ArrayHelper::merge(
182
            $this->getDynamicAttributes(),
183
            $mainModelAttributes
184
        );
185
    }
186
187
    /**
188
     * Saves data in the main model.
189
     *
190
     * @return bool
191
     */
192
    public function save(): bool
193
    {
194
        if ($this->mainModel->isNewRecord) {
0 ignored issues
show
Bug Best Practice introduced by
The property isNewRecord does not exist on Itstructure\AdminModule\models\MultilanguageTrait. Since you implemented __get, consider adding a @property annotation.
Loading history...
195
            $this->setScenario(ModelInterface::SCENARIO_CREATE);
196
        } else {
197
            $this->setScenario(ModelInterface::SCENARIO_UPDATE);
198
        }
199
200
        if (!$this->validate()){
201
            return false;
202
        }
203
204
        // Transferring attribute values from this model to the main.
205
        foreach ($this->attributes() as $attribute) {
206
207
            $this->mainModel->{$attribute} = $this->{$attribute};
208
        }
209
210
        return $this->mainModel->save();
0 ignored issues
show
It seems like save() 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

210
        return $this->mainModel->/** @scrutinizer ignore-call */ save();
Loading history...
211
    }
212
213
    /**
214
     * Returns the id of the current model.
215
     *
216
     * @return int
217
     */
218
    public function getId()
219
    {
220
        return $this->mainModel->id;
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on Itstructure\AdminModule\models\MultilanguageTrait. Since you implemented __get, consider adding a @property annotation.
Loading history...
221
    }
222
223
    /**
224
     * Returns an array of all multilanguage attributes.
225
     *
226
     * @return array
227
     */
228
    private function getDynamicAttributes(): array
229
    {
230
        $languageList = $this->getShortLanguageList();
231
        $dynamicAttributes = [];
232
        foreach ($languageList as $language) {
233
            foreach ($this->dynamicFields as $fieldConditions) {
234
                $dynamicAttributes[] = $fieldConditions['name'] . '_' . $language;
235
            }
236
        }
237
        return array_values($dynamicAttributes);
238
    }
239
240
    /**
241
     * Creates validation rules for dynamic fields for all languages.
242
     *
243
     * @return array
244
     */
245
    private function getDynamicValidationRules(): array
246
    {
247
        $rules = [];
248
        foreach ($this->getShortLanguageList() as $language) {
249
            foreach ($this->dynamicFields as $fieldConditions) {
250
251
                $fieldName = $fieldConditions['name'];
252
                $fieldRules = isset($fieldConditions['rules']) ? $fieldConditions['rules'] : [];
253
254
                foreach ($fieldRules as $fieldRule) {
255
256
                    if (in_array('required', $fieldRule) && $language != Language::getDefaultLanguage()->shortName) {
257
                        continue;
258
                    }
259
260
                    if (in_array('unique', $fieldRule)) {
261
                        $fieldRule = ArrayHelper::merge(
262
                            $fieldRule,
263
                            [
264
                                'skipOnError'     => true,
265
                                'targetClass'     => $this->mainModel->getTranslateModelName(),
266
                                'targetAttribute' => [$fieldName . '_' . $language => $fieldName],
267
268
                                'filter' => $this->getScenario() == ModelInterface::SCENARIO_UPDATE ?
269
                                    $this->mainModel->getKeyToMainModel().' != '.$this->id : '',
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on Itstructure\AdminModule\...tilanguageValidateModel. Since you implemented __get, consider adding a @property annotation.
Loading history...
270
271
                                'message' => isset($fieldRule['message']) ?
272
                                    $fieldRule['message'] : 'Record with such attribute "{attribute}" already exists'
273
                            ]
274
                        );
275
                    }
276
277
                    $rules[] = ArrayHelper::merge(
278
                        [$fieldName . '_' . $language],
279
                        $fieldRule
280
                    );
281
                }
282
            }
283
        }
284
285
        return $rules;
286
    }
287
288
    /**
289
     * Returns the list of available languages in the short name format.
290
     *
291
     * @return array
292
     */
293
    private function getShortLanguageList(): array
294
    {
295
        return Language::getShortLanguageList();
296
    }
297
}
298