1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Created by PhpStorm. |
4
|
|
|
* User: floor12 |
5
|
|
|
* Date: 31.12.2017 |
6
|
|
|
* Time: 15:14 |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace floor12\files\components; |
10
|
|
|
|
11
|
|
|
|
12
|
|
|
use floor12\files\models\File; |
13
|
|
|
use Yii; |
14
|
|
|
use yii\base\Behavior; |
15
|
|
|
use yii\base\ErrorException; |
16
|
|
|
use yii\db\ActiveRecord; |
17
|
|
|
use yii\db\Expression; |
18
|
|
|
use yii\validators\Validator; |
19
|
|
|
|
20
|
|
|
class FileBehaviour extends Behavior |
21
|
|
|
{ |
22
|
|
|
/** Массив для хранения файловых атрибутов и их параметров. |
23
|
|
|
* Задается через Behaviors в моделе |
24
|
|
|
* @var array |
25
|
|
|
*/ |
26
|
|
|
public $attributes = []; |
27
|
|
|
|
28
|
|
|
/** В этот массив помещаются id связанных файлов с текущей моделью для последующейго сохранения. |
29
|
|
|
* @var array |
30
|
|
|
*/ |
31
|
|
|
private $_values = []; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Вещаем сохранение данных на события. |
35
|
|
|
*/ |
36
|
|
|
public function events() |
37
|
|
|
{ |
38
|
|
|
return [ |
39
|
|
|
ActiveRecord::EVENT_AFTER_INSERT => 'filesSave', |
40
|
|
|
ActiveRecord::EVENT_AFTER_UPDATE => 'filesSave', |
41
|
|
|
ActiveRecord::EVENT_AFTER_DELETE => 'filesDelete', |
42
|
|
|
ActiveRecord::EVENT_BEFORE_VALIDATE => 'validateRequiredFields' |
43
|
|
|
]; |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
protected $cachedFiles = []; |
47
|
|
|
|
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Метод сохранения в базу связей с файлами. Вызывается после сохранения основной модели AR. |
51
|
|
|
* @throws ErrorException |
52
|
|
|
* @throws \yii\db\Exception |
53
|
|
|
*/ |
54
|
|
|
|
55
|
|
|
public function filesSave() |
56
|
|
|
{ |
57
|
|
|
$order = 0; |
58
|
|
|
if ($this->_values) { |
|
|
|
|
59
|
|
|
|
60
|
|
|
foreach ($this->_values as $field => $ids) { |
61
|
|
|
|
62
|
|
|
Yii::$app->db->createCommand()->update( |
63
|
|
|
"{{%file}}", |
64
|
|
|
['object_id' => 0], |
65
|
|
|
[ |
66
|
|
|
'class' => $this->owner->className(), |
|
|
|
|
67
|
|
|
'object_id' => $this->owner->id, |
68
|
|
|
'field' => $field, |
69
|
|
|
] |
70
|
|
|
)->execute(); |
71
|
|
|
|
72
|
|
|
if ($ids) foreach ($ids as $id) { |
73
|
|
|
if (empty($id)) |
74
|
|
|
continue; |
75
|
|
|
$file = File::findOne($id); |
76
|
|
|
if ($file) { |
77
|
|
|
$file->object_id = $this->owner->id; |
78
|
|
|
$file->ordering = $order++; |
79
|
|
|
$file->save(); |
80
|
|
|
if (!$file->save()) { |
81
|
|
|
throw new ErrorException('Невозможно обновить объект File.'); |
82
|
|
|
} |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
public function filesDelete() |
91
|
|
|
{ |
92
|
|
|
File::deleteAll([ |
93
|
|
|
'class' => $this->owner->className(), |
|
|
|
|
94
|
|
|
'object_id' => $this->owner->id, |
95
|
|
|
]); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
public function validateRequiredFields() |
99
|
|
|
{ |
100
|
|
|
foreach ($this->attributes as $attributeName => $params) { |
101
|
|
|
$attributeIds = $this->getRealAttributeName($attributeName); |
102
|
|
|
|
103
|
|
|
if ( |
104
|
|
|
isset($params['required']) && |
105
|
|
|
$params['required'] && |
106
|
|
|
in_array($this->owner->scenario, $params['requiredOn']) && |
107
|
|
|
!in_array($this->owner->scenario, $params['requiredExcept']) && |
108
|
|
|
!isset($this->_values[$attributeIds][1]) |
109
|
|
|
) |
110
|
|
|
$this->owner->addError($attributeName, $params['requiredMessage']); |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Устанавливаем валидаторы. |
116
|
|
|
* @param ActiveRecord $owner |
117
|
|
|
*/ |
118
|
|
|
public |
119
|
|
|
function attach($owner) |
120
|
|
|
{ |
121
|
|
|
parent::attach($owner); |
122
|
|
|
|
123
|
|
|
// Получаем валидаторы AR |
124
|
|
|
$validators = $owner->validators; |
125
|
|
|
|
126
|
|
|
// Пробегаемся по валидаторам и вычисляем, какие из них касаются наших файл-полей |
127
|
|
|
if ($validators) |
128
|
|
|
foreach ($validators as $key => $validator) { |
129
|
|
|
|
130
|
|
|
// Сначала пробегаемся по файловым валидаторам |
131
|
|
|
if ($validator::className() == 'yii\validators\FileValidator' || $validator::className() == 'floor12\files\components\ReformatValidator') { |
|
|
|
|
132
|
|
|
foreach ($this->attributes as $field => $params) { |
133
|
|
|
|
134
|
|
|
if (is_string($params)) { |
135
|
|
|
$field = $params; |
136
|
|
|
$params = []; |
|
|
|
|
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
$index = array_search($field, $validator->attributes); |
|
|
|
|
140
|
|
|
if ($index !== false) { |
141
|
|
|
$this->attributes[$field]['validator'][$validator::className()] = $validator; |
|
|
|
|
142
|
|
|
unset($validator->attributes[$index]); |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
|
148
|
|
|
if ($validator::className() == 'yii\validators\RequiredValidator') { |
|
|
|
|
149
|
|
|
foreach ($this->attributes as $field => $params) { |
150
|
|
|
|
151
|
|
|
if (is_string($params)) { |
152
|
|
|
$field = $params; |
153
|
|
|
$params = []; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$index = array_search($field, $validator->attributes); |
157
|
|
|
if ($index !== false) { |
158
|
|
|
unset($validator->attributes[$index]); |
159
|
|
|
$this->attributes[$field]['required'] = true; |
160
|
|
|
$this->attributes[$field]['requiredExcept'] = $validator->except; |
161
|
|
|
$this->attributes[$field]['requiredOn'] = sizeof($validator->on) ? $validator->on : [ActiveRecord::SCENARIO_DEFAULT]; |
|
|
|
|
162
|
|
|
$this->attributes[$field]['requiredMessage'] = str_replace("{attribute}", $this->owner->getAttributeLabel($field), $validator->message); |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
|
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
// Добавляем дефолтный валидатор для прилетающих айдишников |
171
|
|
|
if ($this->attributes) foreach ($this->attributes as $fieldName => $fieldParams) { |
|
|
|
|
172
|
|
|
$validator = Validator::createValidator('safe', $owner, ["{$fieldName}_ids"]); |
173
|
|
|
$validators->append($validator); |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* @inheritdoc |
180
|
|
|
*/ |
181
|
|
|
public function canGetProperty($name, $checkVars = true) |
182
|
|
|
{ |
183
|
|
|
return array_key_exists($name, $this->attributes) ? |
184
|
|
|
true : parent::canGetProperty($name, $checkVars); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @inheritdoc |
190
|
|
|
*/ |
191
|
|
|
public function canSetProperty($name, $checkVars = true) |
192
|
|
|
{ |
193
|
|
|
if (array_key_exists($this->getRealAttributeName($name), $this->attributes)) |
194
|
|
|
return true; |
195
|
|
|
|
196
|
|
|
return parent::canSetProperty($name, $checkVars = true); |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* @inheritdoc |
202
|
|
|
*/ |
203
|
|
|
public function __get($att_name) |
204
|
|
|
{ |
205
|
|
|
if (isset($this->_values[$att_name])) { |
206
|
|
|
unset($this->_values[$att_name][0]); |
207
|
|
|
if (sizeof($this->_values[$att_name])) |
208
|
|
|
return array_map(function ($fileId) { |
209
|
|
|
return File::findOne($fileId); |
210
|
|
|
}, $this->_values[$att_name]); |
211
|
|
|
} else { |
212
|
|
|
if (!isset($this->cachedFiles[$att_name])) { |
213
|
|
|
if ( |
214
|
|
|
isset($this->attributes[$att_name]['validator']) && |
215
|
|
|
isset($this->attributes[$att_name]['validator']['yii\validators\FileValidator']) && |
216
|
|
|
$this->attributes[$att_name]['validator']['yii\validators\FileValidator']->maxFiles > 1 |
217
|
|
|
) |
218
|
|
|
$this->cachedFiles[$att_name] = File::find() |
219
|
|
|
->where( |
220
|
|
|
[ |
221
|
|
|
'object_id' => $this->owner->id, |
222
|
|
|
'field' => $att_name, |
223
|
|
|
'class' => $this->owner->className() |
|
|
|
|
224
|
|
|
]) |
225
|
|
|
->orderBy('ordering ASC') |
226
|
|
|
->all(); |
227
|
|
|
else { |
228
|
|
|
$this->cachedFiles[$att_name] = File::find() |
229
|
|
|
->where( |
230
|
|
|
[ |
231
|
|
|
'object_id' => $this->owner->id, |
232
|
|
|
'field' => $att_name, |
233
|
|
|
'class' => $this->owner->className() |
|
|
|
|
234
|
|
|
]) |
235
|
|
|
->orderBy('ordering ASC') |
236
|
|
|
->one(); |
237
|
|
|
} |
238
|
|
|
} |
239
|
|
|
return $this->cachedFiles[$att_name]; |
240
|
|
|
} |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* @inheritdoc |
246
|
|
|
*/ |
247
|
|
|
public |
248
|
|
|
function __set($name, $value) |
249
|
|
|
{ |
250
|
|
|
$attribute = $this->getRealAttributeName($name); |
251
|
|
|
|
252
|
|
|
if (array_key_exists($attribute, $this->attributes)) |
253
|
|
|
$this->_values[$attribute] = $value; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
|
257
|
|
|
/** Отбрасываем постфикс _ids |
258
|
|
|
* @param $attribute string |
259
|
|
|
* @return string |
260
|
|
|
*/ |
261
|
|
|
private |
262
|
|
|
function getRealAttributeName($attribute) |
263
|
|
|
{ |
264
|
|
|
return str_replace("_ids", "", $attribute); |
265
|
|
|
} |
266
|
|
|
} |
267
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.