|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* @link https://www.yiiframework.com/ |
|
4
|
|
|
* @copyright Copyright (c) 2008 Yii Software LLC |
|
5
|
|
|
* @license https://www.yiiframework.com/license/ |
|
6
|
|
|
*/ |
|
7
|
|
|
|
|
8
|
|
|
namespace yii\base; |
|
9
|
|
|
|
|
10
|
|
|
use ArrayAccess; |
|
11
|
|
|
use ArrayIterator; |
|
12
|
|
|
use ArrayObject; |
|
13
|
|
|
use IteratorAggregate; |
|
14
|
|
|
use ReflectionClass; |
|
15
|
|
|
use Yii; |
|
16
|
|
|
use yii\helpers\Inflector; |
|
17
|
|
|
use yii\validators\RequiredValidator; |
|
18
|
|
|
use yii\validators\Validator; |
|
19
|
|
|
|
|
20
|
|
|
/** |
|
21
|
|
|
* Model is the base class for data models. |
|
22
|
|
|
* |
|
23
|
|
|
* Model implements the following commonly used features: |
|
24
|
|
|
* |
|
25
|
|
|
* - attribute declaration: by default, every public class member is considered as |
|
26
|
|
|
* a model attribute |
|
27
|
|
|
* - attribute labels: each attribute may be associated with a label for display purpose |
|
28
|
|
|
* - massive attribute assignment |
|
29
|
|
|
* - scenario-based validation |
|
30
|
|
|
* |
|
31
|
|
|
* Model also raises the following events when performing data validation: |
|
32
|
|
|
* |
|
33
|
|
|
* - [[EVENT_BEFORE_VALIDATE]]: an event raised at the beginning of [[validate()]] |
|
34
|
|
|
* - [[EVENT_AFTER_VALIDATE]]: an event raised at the end of [[validate()]] |
|
35
|
|
|
* |
|
36
|
|
|
* You may directly use Model to store model data, or extend it with customization. |
|
37
|
|
|
* |
|
38
|
|
|
* For more details and usage information on Model, see the [guide article on models](guide:structure-models). |
|
39
|
|
|
* |
|
40
|
|
|
* @property-read \yii\validators\Validator[] $activeValidators The validators applicable to the current |
|
41
|
|
|
* [[scenario]]. |
|
42
|
|
|
* @property array $attributes Attribute values (name => value). |
|
43
|
|
|
* @property-read array $errors Errors for all attributes or the specified attribute. Empty array is returned |
|
44
|
|
|
* if no error. See [[getErrors()]] for detailed description. Note that when returning errors for all attributes, |
|
45
|
|
|
* the result is a two-dimensional array, like the following: ```php [ 'username' => [ 'Username is required.', |
|
46
|
|
|
* 'Username must contain only word characters.', ], 'email' => [ 'Email address is invalid.', ] ] ``` . |
|
47
|
|
|
* @property-read array $firstErrors The first errors. The array keys are the attribute names, and the array |
|
48
|
|
|
* values are the corresponding error messages. An empty array will be returned if there is no error. |
|
49
|
|
|
* @property string $scenario The scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]]. |
|
50
|
|
|
* @property-read ArrayObject|\yii\validators\Validator[] $validators All the validators declared in the |
|
51
|
|
|
* model. |
|
52
|
|
|
* |
|
53
|
|
|
* @author Qiang Xue <[email protected]> |
|
54
|
|
|
* @since 2.0 |
|
55
|
|
|
*/ |
|
56
|
|
|
class Model extends Component implements StaticInstanceInterface, IteratorAggregate, ArrayAccess, Arrayable |
|
57
|
|
|
{ |
|
58
|
|
|
use ArrayableTrait; |
|
59
|
|
|
use StaticInstanceTrait; |
|
60
|
|
|
|
|
61
|
|
|
/** |
|
62
|
|
|
* The name of the default scenario. |
|
63
|
|
|
*/ |
|
64
|
|
|
const SCENARIO_DEFAULT = 'default'; |
|
65
|
|
|
/** |
|
66
|
|
|
* @event ModelEvent an event raised at the beginning of [[validate()]]. You may set |
|
67
|
|
|
* [[ModelEvent::isValid]] to be false to stop the validation. |
|
68
|
|
|
*/ |
|
69
|
|
|
const EVENT_BEFORE_VALIDATE = 'beforeValidate'; |
|
70
|
|
|
/** |
|
71
|
|
|
* @event Event an event raised at the end of [[validate()]] |
|
72
|
|
|
*/ |
|
73
|
|
|
const EVENT_AFTER_VALIDATE = 'afterValidate'; |
|
74
|
|
|
|
|
75
|
|
|
/** |
|
76
|
|
|
* @var array validation errors (attribute name => array of errors) |
|
77
|
|
|
*/ |
|
78
|
|
|
private $_errors; |
|
79
|
|
|
/** |
|
80
|
|
|
* @var ArrayObject list of validators |
|
81
|
|
|
*/ |
|
82
|
|
|
private $_validators; |
|
83
|
|
|
/** |
|
84
|
|
|
* @var string current scenario |
|
85
|
|
|
*/ |
|
86
|
|
|
private $_scenario = self::SCENARIO_DEFAULT; |
|
87
|
|
|
|
|
88
|
|
|
|
|
89
|
|
|
/** |
|
90
|
|
|
* Returns the validation rules for attributes. |
|
91
|
|
|
* |
|
92
|
|
|
* Validation rules are used by [[validate()]] to check if attribute values are valid. |
|
93
|
|
|
* Child classes may override this method to declare different validation rules. |
|
94
|
|
|
* |
|
95
|
|
|
* Each rule is an array with the following structure: |
|
96
|
|
|
* |
|
97
|
|
|
* ```php |
|
98
|
|
|
* [ |
|
99
|
|
|
* ['attribute1', 'attribute2'], |
|
100
|
|
|
* 'validator type', |
|
101
|
|
|
* 'on' => ['scenario1', 'scenario2'], |
|
102
|
|
|
* //...other parameters... |
|
103
|
|
|
* ] |
|
104
|
|
|
* ``` |
|
105
|
|
|
* |
|
106
|
|
|
* where |
|
107
|
|
|
* |
|
108
|
|
|
* - attribute list: required, specifies the attributes array to be validated, for single attribute you can pass a string; |
|
109
|
|
|
* - validator type: required, specifies the validator to be used. It can be a built-in validator name, |
|
110
|
|
|
* a method name of the model class, an anonymous function, or a validator class name. |
|
111
|
|
|
* - on: optional, specifies the [[scenario|scenarios]] array in which the validation |
|
112
|
|
|
* rule can be applied. If this option is not set, the rule will apply to all scenarios. |
|
113
|
|
|
* - additional name-value pairs can be specified to initialize the corresponding validator properties. |
|
114
|
|
|
* Please refer to individual validator class API for possible properties. |
|
115
|
|
|
* |
|
116
|
|
|
* A validator can be either an object of a class extending [[Validator]], or a model class method |
|
117
|
|
|
* (called *inline validator*) that has the following signature: |
|
118
|
|
|
* |
|
119
|
|
|
* ```php |
|
120
|
|
|
* // $params refers to validation parameters given in the rule |
|
121
|
|
|
* function validatorName($attribute, $params) |
|
122
|
|
|
* ``` |
|
123
|
|
|
* |
|
124
|
|
|
* In the above `$attribute` refers to the attribute currently being validated while `$params` contains an array of |
|
125
|
|
|
* validator configuration options such as `max` in case of `string` validator. The value of the attribute currently being validated |
|
126
|
|
|
* can be accessed as `$this->$attribute`. Note the `$` before `attribute`; this is taking the value of the variable |
|
127
|
|
|
* `$attribute` and using it as the name of the property to access. |
|
128
|
|
|
* |
|
129
|
|
|
* Yii also provides a set of [[Validator::builtInValidators|built-in validators]]. |
|
130
|
|
|
* Each one has an alias name which can be used when specifying a validation rule. |
|
131
|
|
|
* |
|
132
|
|
|
* Below are some examples: |
|
133
|
|
|
* |
|
134
|
|
|
* ```php |
|
135
|
|
|
* [ |
|
136
|
|
|
* // built-in "required" validator |
|
137
|
|
|
* [['username', 'password'], 'required'], |
|
138
|
|
|
* // built-in "string" validator customized with "min" and "max" properties |
|
139
|
|
|
* ['username', 'string', 'min' => 3, 'max' => 12], |
|
140
|
|
|
* // built-in "compare" validator that is used in "register" scenario only |
|
141
|
|
|
* ['password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'], |
|
142
|
|
|
* // an inline validator defined via the "authenticate()" method in the model class |
|
143
|
|
|
* ['password', 'authenticate', 'on' => 'login'], |
|
144
|
|
|
* // a validator of class "DateRangeValidator" |
|
145
|
|
|
* ['dateRange', 'DateRangeValidator'], |
|
146
|
|
|
* ]; |
|
147
|
|
|
* ``` |
|
148
|
|
|
* |
|
149
|
|
|
* Note, in order to inherit rules defined in the parent class, a child class needs to |
|
150
|
|
|
* merge the parent rules with child rules using functions such as `array_merge()`. |
|
151
|
|
|
* |
|
152
|
|
|
* @return array validation rules |
|
153
|
|
|
* @see scenarios() |
|
154
|
|
|
*/ |
|
155
|
150 |
|
public function rules() |
|
156
|
|
|
{ |
|
157
|
150 |
|
return []; |
|
158
|
|
|
} |
|
159
|
|
|
|
|
160
|
|
|
/** |
|
161
|
|
|
* Returns a list of scenarios and the corresponding active attributes. |
|
162
|
|
|
* |
|
163
|
|
|
* An active attribute is one that is subject to validation in the current scenario. |
|
164
|
|
|
* The returned array should be in the following format: |
|
165
|
|
|
* |
|
166
|
|
|
* ```php |
|
167
|
|
|
* [ |
|
168
|
|
|
* 'scenario1' => ['attribute11', 'attribute12', ...], |
|
169
|
|
|
* 'scenario2' => ['attribute21', 'attribute22', ...], |
|
170
|
|
|
* ... |
|
171
|
|
|
* ] |
|
172
|
|
|
* ``` |
|
173
|
|
|
* |
|
174
|
|
|
* By default, an active attribute is considered safe and can be massively assigned. |
|
175
|
|
|
* If an attribute should NOT be massively assigned (thus considered unsafe), |
|
176
|
|
|
* please prefix the attribute with an exclamation character (e.g. `'!rank'`). |
|
177
|
|
|
* |
|
178
|
|
|
* The default implementation of this method will return all scenarios found in the [[rules()]] |
|
179
|
|
|
* declaration. A special scenario named [[SCENARIO_DEFAULT]] will contain all attributes |
|
180
|
|
|
* found in the [[rules()]]. Each scenario will be associated with the attributes that |
|
181
|
|
|
* are being validated by the validation rules that apply to the scenario. |
|
182
|
|
|
* |
|
183
|
|
|
* @return array a list of scenarios and the corresponding active attributes. |
|
184
|
|
|
*/ |
|
185
|
162 |
|
public function scenarios() |
|
186
|
|
|
{ |
|
187
|
162 |
|
$scenarios = [self::SCENARIO_DEFAULT => []]; |
|
188
|
162 |
|
foreach ($this->getValidators() as $validator) { |
|
189
|
84 |
|
foreach ($validator->on as $scenario) { |
|
190
|
6 |
|
$scenarios[$scenario] = []; |
|
191
|
|
|
} |
|
192
|
84 |
|
foreach ($validator->except as $scenario) { |
|
193
|
2 |
|
$scenarios[$scenario] = []; |
|
194
|
|
|
} |
|
195
|
|
|
} |
|
196
|
162 |
|
$names = array_keys($scenarios); |
|
197
|
|
|
|
|
198
|
162 |
|
foreach ($this->getValidators() as $validator) { |
|
199
|
84 |
|
if (empty($validator->on) && empty($validator->except)) { |
|
200
|
84 |
|
foreach ($names as $name) { |
|
201
|
84 |
|
foreach ($validator->attributes as $attribute) { |
|
202
|
84 |
|
$scenarios[$name][$attribute] = true; |
|
203
|
|
|
} |
|
204
|
|
|
} |
|
205
|
6 |
|
} elseif (empty($validator->on)) { |
|
206
|
2 |
|
foreach ($names as $name) { |
|
207
|
2 |
|
if (!in_array($name, $validator->except, true)) { |
|
208
|
2 |
|
foreach ($validator->attributes as $attribute) { |
|
209
|
2 |
|
$scenarios[$name][$attribute] = true; |
|
210
|
|
|
} |
|
211
|
|
|
} |
|
212
|
|
|
} |
|
213
|
|
|
} else { |
|
214
|
6 |
|
foreach ($validator->on as $name) { |
|
215
|
6 |
|
foreach ($validator->attributes as $attribute) { |
|
216
|
6 |
|
$scenarios[$name][$attribute] = true; |
|
217
|
|
|
} |
|
218
|
|
|
} |
|
219
|
|
|
} |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
162 |
|
foreach ($scenarios as $scenario => $attributes) { |
|
223
|
162 |
|
if (!empty($attributes)) { |
|
224
|
84 |
|
$scenarios[$scenario] = array_keys($attributes); |
|
225
|
|
|
} |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
162 |
|
return $scenarios; |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
/** |
|
232
|
|
|
* Returns the form name that this model class should use. |
|
233
|
|
|
* |
|
234
|
|
|
* The form name is mainly used by [[\yii\widgets\ActiveForm]] to determine how to name |
|
235
|
|
|
* the input fields for the attributes in a model. If the form name is "A" and an attribute |
|
236
|
|
|
* name is "b", then the corresponding input name would be "A[b]". If the form name is |
|
237
|
|
|
* an empty string, then the input name would be "b". |
|
238
|
|
|
* |
|
239
|
|
|
* The purpose of the above naming schema is that for forms which contain multiple different models, |
|
240
|
|
|
* the attributes of each model are grouped in sub-arrays of the POST-data and it is easier to |
|
241
|
|
|
* differentiate between them. |
|
242
|
|
|
* |
|
243
|
|
|
* By default, this method returns the model class name (without the namespace part) |
|
244
|
|
|
* as the form name. You may override it when the model is used in different forms. |
|
245
|
|
|
* |
|
246
|
|
|
* @return string the form name of this model class. |
|
247
|
|
|
* @see load() |
|
248
|
|
|
* @throws InvalidConfigException when form is defined with anonymous class and `formName()` method is |
|
249
|
|
|
* not overridden. |
|
250
|
|
|
*/ |
|
251
|
97 |
|
public function formName() |
|
252
|
|
|
{ |
|
253
|
97 |
|
$reflector = new ReflectionClass($this); |
|
254
|
97 |
|
if (PHP_VERSION_ID >= 70000 && $reflector->isAnonymous()) { |
|
255
|
1 |
|
throw new InvalidConfigException('The "formName()" method should be explicitly defined for anonymous models'); |
|
256
|
|
|
} |
|
257
|
96 |
|
return $reflector->getShortName(); |
|
258
|
|
|
} |
|
259
|
|
|
|
|
260
|
|
|
/** |
|
261
|
|
|
* Returns the list of attribute names. |
|
262
|
|
|
* |
|
263
|
|
|
* By default, this method returns all public non-static properties of the class. |
|
264
|
|
|
* You may override this method to change the default behavior. |
|
265
|
|
|
* |
|
266
|
|
|
* @return string[] list of attribute names. |
|
267
|
|
|
*/ |
|
268
|
9 |
|
public function attributes() |
|
269
|
|
|
{ |
|
270
|
9 |
|
$class = new ReflectionClass($this); |
|
271
|
9 |
|
$names = []; |
|
272
|
9 |
|
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { |
|
273
|
9 |
|
if (!$property->isStatic()) { |
|
274
|
9 |
|
$names[] = $property->getName(); |
|
275
|
|
|
} |
|
276
|
|
|
} |
|
277
|
|
|
|
|
278
|
9 |
|
return $names; |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
/** |
|
282
|
|
|
* Returns the attribute labels. |
|
283
|
|
|
* |
|
284
|
|
|
* Attribute labels are mainly used for display purpose. For example, given an attribute |
|
285
|
|
|
* `firstName`, we can declare a label `First Name` which is more user-friendly and can |
|
286
|
|
|
* be displayed to end users. |
|
287
|
|
|
* |
|
288
|
|
|
* By default an attribute label is generated using [[generateAttributeLabel()]]. |
|
289
|
|
|
* This method allows you to explicitly specify attribute labels. |
|
290
|
|
|
* |
|
291
|
|
|
* Note, in order to inherit labels defined in the parent class, a child class needs to |
|
292
|
|
|
* merge the parent labels with child labels using functions such as `array_merge()`. |
|
293
|
|
|
* |
|
294
|
|
|
* @return array attribute labels (name => label) |
|
295
|
|
|
* @see generateAttributeLabel() |
|
296
|
|
|
*/ |
|
297
|
73 |
|
public function attributeLabels() |
|
298
|
|
|
{ |
|
299
|
73 |
|
return []; |
|
300
|
|
|
} |
|
301
|
|
|
|
|
302
|
|
|
/** |
|
303
|
|
|
* Returns the attribute hints. |
|
304
|
|
|
* |
|
305
|
|
|
* Attribute hints are mainly used for display purpose. For example, given an attribute |
|
306
|
|
|
* `isPublic`, we can declare a hint `Whether the post should be visible for not logged in users`, |
|
307
|
|
|
* which provides user-friendly description of the attribute meaning and can be displayed to end users. |
|
308
|
|
|
* |
|
309
|
|
|
* Unlike label hint will not be generated, if its explicit declaration is omitted. |
|
310
|
|
|
* |
|
311
|
|
|
* Note, in order to inherit hints defined in the parent class, a child class needs to |
|
312
|
|
|
* merge the parent hints with child hints using functions such as `array_merge()`. |
|
313
|
|
|
* |
|
314
|
|
|
* @return array attribute hints (name => hint) |
|
315
|
|
|
* @since 2.0.4 |
|
316
|
|
|
*/ |
|
317
|
4 |
|
public function attributeHints() |
|
318
|
|
|
{ |
|
319
|
4 |
|
return []; |
|
320
|
|
|
} |
|
321
|
|
|
|
|
322
|
|
|
/** |
|
323
|
|
|
* Performs the data validation. |
|
324
|
|
|
* |
|
325
|
|
|
* This method executes the validation rules applicable to the current [[scenario]]. |
|
326
|
|
|
* The following criteria are used to determine whether a rule is currently applicable: |
|
327
|
|
|
* |
|
328
|
|
|
* - the rule must be associated with the attributes relevant to the current scenario; |
|
329
|
|
|
* - the rules must be effective for the current scenario. |
|
330
|
|
|
* |
|
331
|
|
|
* This method will call [[beforeValidate()]] and [[afterValidate()]] before and |
|
332
|
|
|
* after the actual validation, respectively. If [[beforeValidate()]] returns false, |
|
333
|
|
|
* the validation will be cancelled and [[afterValidate()]] will not be called. |
|
334
|
|
|
* |
|
335
|
|
|
* Errors found during the validation can be retrieved via [[getErrors()]], |
|
336
|
|
|
* [[getFirstErrors()]] and [[getFirstError()]]. |
|
337
|
|
|
* |
|
338
|
|
|
* @param string[]|string|null $attributeNames attribute name or list of attribute names |
|
339
|
|
|
* that should be validated. If this parameter is empty, it means any attribute listed in |
|
340
|
|
|
* the applicable validation rules should be validated. |
|
341
|
|
|
* @param bool $clearErrors whether to call [[clearErrors()]] before performing validation |
|
342
|
|
|
* @return bool whether the validation is successful without any error. |
|
343
|
|
|
* @throws InvalidArgumentException if the current scenario is unknown. |
|
344
|
|
|
*/ |
|
345
|
115 |
|
public function validate($attributeNames = null, $clearErrors = true) |
|
346
|
|
|
{ |
|
347
|
115 |
|
if ($clearErrors) { |
|
348
|
105 |
|
$this->clearErrors(); |
|
349
|
|
|
} |
|
350
|
|
|
|
|
351
|
115 |
|
if (!$this->beforeValidate()) { |
|
352
|
|
|
return false; |
|
353
|
|
|
} |
|
354
|
|
|
|
|
355
|
115 |
|
$scenarios = $this->scenarios(); |
|
356
|
115 |
|
$scenario = $this->getScenario(); |
|
357
|
115 |
|
if (!isset($scenarios[$scenario])) { |
|
358
|
|
|
throw new InvalidArgumentException("Unknown scenario: $scenario"); |
|
359
|
|
|
} |
|
360
|
|
|
|
|
361
|
115 |
|
if ($attributeNames === null) { |
|
362
|
114 |
|
$attributeNames = $this->activeAttributes(); |
|
363
|
|
|
} |
|
364
|
|
|
|
|
365
|
115 |
|
$attributeNames = (array)$attributeNames; |
|
366
|
|
|
|
|
367
|
115 |
|
foreach ($this->getActiveValidators() as $validator) { |
|
368
|
62 |
|
$validator->validateAttributes($this, $attributeNames); |
|
369
|
|
|
} |
|
370
|
115 |
|
$this->afterValidate(); |
|
371
|
|
|
|
|
372
|
115 |
|
return !$this->hasErrors(); |
|
373
|
|
|
} |
|
374
|
|
|
|
|
375
|
|
|
/** |
|
376
|
|
|
* This method is invoked before validation starts. |
|
377
|
|
|
* The default implementation raises a `beforeValidate` event. |
|
378
|
|
|
* You may override this method to do preliminary checks before validation. |
|
379
|
|
|
* Make sure the parent implementation is invoked so that the event can be raised. |
|
380
|
|
|
* @return bool whether the validation should be executed. Defaults to true. |
|
381
|
|
|
* If false is returned, the validation will stop and the model is considered invalid. |
|
382
|
|
|
*/ |
|
383
|
116 |
|
public function beforeValidate() |
|
384
|
|
|
{ |
|
385
|
116 |
|
$event = new ModelEvent(); |
|
386
|
116 |
|
$this->trigger(self::EVENT_BEFORE_VALIDATE, $event); |
|
387
|
|
|
|
|
388
|
116 |
|
return $event->isValid; |
|
389
|
|
|
} |
|
390
|
|
|
|
|
391
|
|
|
/** |
|
392
|
|
|
* This method is invoked after validation ends. |
|
393
|
|
|
* The default implementation raises an `afterValidate` event. |
|
394
|
|
|
* You may override this method to do postprocessing after validation. |
|
395
|
|
|
* Make sure the parent implementation is invoked so that the event can be raised. |
|
396
|
|
|
*/ |
|
397
|
115 |
|
public function afterValidate() |
|
398
|
|
|
{ |
|
399
|
115 |
|
$this->trigger(self::EVENT_AFTER_VALIDATE); |
|
400
|
|
|
} |
|
401
|
|
|
|
|
402
|
|
|
/** |
|
403
|
|
|
* Returns all the validators declared in [[rules()]]. |
|
404
|
|
|
* |
|
405
|
|
|
* This method differs from [[getActiveValidators()]] in that the latter |
|
406
|
|
|
* only returns the validators applicable to the current [[scenario]]. |
|
407
|
|
|
* |
|
408
|
|
|
* Because this method returns an ArrayObject object, you may |
|
409
|
|
|
* manipulate it by inserting or removing validators (useful in model behaviors). |
|
410
|
|
|
* For example, |
|
411
|
|
|
* |
|
412
|
|
|
* ```php |
|
413
|
|
|
* $model->validators[] = $newValidator; |
|
414
|
|
|
* ``` |
|
415
|
|
|
* |
|
416
|
|
|
* @return ArrayObject|\yii\validators\Validator[] all the validators declared in the model. |
|
417
|
|
|
*/ |
|
418
|
180 |
|
public function getValidators() |
|
419
|
|
|
{ |
|
420
|
180 |
|
if ($this->_validators === null) { |
|
421
|
180 |
|
$this->_validators = $this->createValidators(); |
|
422
|
|
|
} |
|
423
|
|
|
|
|
424
|
180 |
|
return $this->_validators; |
|
425
|
|
|
} |
|
426
|
|
|
|
|
427
|
|
|
/** |
|
428
|
|
|
* Returns the validators applicable to the current [[scenario]]. |
|
429
|
|
|
* @param string|null $attribute the name of the attribute whose applicable validators should be returned. |
|
430
|
|
|
* If this is null, the validators for ALL attributes in the model will be returned. |
|
431
|
|
|
* @return \yii\validators\Validator[] the validators applicable to the current [[scenario]]. |
|
432
|
|
|
*/ |
|
433
|
156 |
|
public function getActiveValidators($attribute = null) |
|
434
|
|
|
{ |
|
435
|
156 |
|
$activeAttributes = $this->activeAttributes(); |
|
436
|
156 |
|
if ($attribute !== null && !in_array($attribute, $activeAttributes, true)) { |
|
437
|
26 |
|
return []; |
|
438
|
|
|
} |
|
439
|
132 |
|
$scenario = $this->getScenario(); |
|
440
|
132 |
|
$validators = []; |
|
441
|
132 |
|
foreach ($this->getValidators() as $validator) { |
|
442
|
79 |
|
if ($attribute === null) { |
|
443
|
63 |
|
$validatorAttributes = $validator->getValidationAttributes($activeAttributes); |
|
444
|
63 |
|
$attributeValid = !empty($validatorAttributes); |
|
445
|
|
|
} else { |
|
446
|
17 |
|
$attributeValid = in_array($attribute, $validator->getValidationAttributes($attribute), true); |
|
447
|
|
|
} |
|
448
|
79 |
|
if ($attributeValid && $validator->isActive($scenario)) { |
|
449
|
79 |
|
$validators[] = $validator; |
|
450
|
|
|
} |
|
451
|
|
|
} |
|
452
|
|
|
|
|
453
|
132 |
|
return $validators; |
|
454
|
|
|
} |
|
455
|
|
|
|
|
456
|
|
|
/** |
|
457
|
|
|
* Creates validator objects based on the validation rules specified in [[rules()]]. |
|
458
|
|
|
* Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned. |
|
459
|
|
|
* @return ArrayObject validators |
|
460
|
|
|
* @throws InvalidConfigException if any validation rule configuration is invalid |
|
461
|
|
|
*/ |
|
462
|
181 |
|
public function createValidators() |
|
463
|
|
|
{ |
|
464
|
181 |
|
$validators = new ArrayObject(); |
|
465
|
181 |
|
foreach ($this->rules() as $rule) { |
|
466
|
61 |
|
if ($rule instanceof Validator) { |
|
467
|
|
|
$validators->append($rule); |
|
468
|
61 |
|
} elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type |
|
469
|
60 |
|
$validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2)); |
|
470
|
60 |
|
$validators->append($validator); |
|
471
|
|
|
} else { |
|
472
|
1 |
|
throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.'); |
|
473
|
|
|
} |
|
474
|
|
|
} |
|
475
|
|
|
|
|
476
|
180 |
|
return $validators; |
|
477
|
|
|
} |
|
478
|
|
|
|
|
479
|
|
|
/** |
|
480
|
|
|
* Returns a value indicating whether the attribute is required. |
|
481
|
|
|
* This is determined by checking if the attribute is associated with a |
|
482
|
|
|
* [[\yii\validators\RequiredValidator|required]] validation rule in the |
|
483
|
|
|
* current [[scenario]]. |
|
484
|
|
|
* |
|
485
|
|
|
* Note that when the validator has a conditional validation applied using |
|
486
|
|
|
* [[\yii\validators\RequiredValidator::$when|$when]] this method will return |
|
487
|
|
|
* `false` regardless of the `when` condition because it may be called be |
|
488
|
|
|
* before the model is loaded with data. |
|
489
|
|
|
* |
|
490
|
|
|
* @param string $attribute attribute name |
|
491
|
|
|
* @return bool whether the attribute is required |
|
492
|
|
|
*/ |
|
493
|
30 |
|
public function isAttributeRequired($attribute) |
|
494
|
|
|
{ |
|
495
|
30 |
|
foreach ($this->getActiveValidators($attribute) as $validator) { |
|
496
|
6 |
|
if ($validator instanceof RequiredValidator && $validator->when === null) { |
|
497
|
5 |
|
return true; |
|
498
|
|
|
} |
|
499
|
|
|
} |
|
500
|
|
|
|
|
501
|
26 |
|
return false; |
|
502
|
|
|
} |
|
503
|
|
|
|
|
504
|
|
|
/** |
|
505
|
|
|
* Returns a value indicating whether the attribute is safe for massive assignments. |
|
506
|
|
|
* @param string $attribute attribute name |
|
507
|
|
|
* @return bool whether the attribute is safe for massive assignments |
|
508
|
|
|
* @see safeAttributes() |
|
509
|
|
|
*/ |
|
510
|
24 |
|
public function isAttributeSafe($attribute) |
|
511
|
|
|
{ |
|
512
|
24 |
|
return in_array($attribute, $this->safeAttributes(), true); |
|
513
|
|
|
} |
|
514
|
|
|
|
|
515
|
|
|
/** |
|
516
|
|
|
* Returns a value indicating whether the attribute is active in the current scenario. |
|
517
|
|
|
* @param string $attribute attribute name |
|
518
|
|
|
* @return bool whether the attribute is active in the current scenario |
|
519
|
|
|
* @see activeAttributes() |
|
520
|
|
|
*/ |
|
521
|
4 |
|
public function isAttributeActive($attribute) |
|
522
|
|
|
{ |
|
523
|
4 |
|
return in_array($attribute, $this->activeAttributes(), true); |
|
524
|
|
|
} |
|
525
|
|
|
|
|
526
|
|
|
/** |
|
527
|
|
|
* Returns the text label for the specified attribute. |
|
528
|
|
|
* @param string $attribute the attribute name |
|
529
|
|
|
* @return string the attribute label |
|
530
|
|
|
* @see generateAttributeLabel() |
|
531
|
|
|
* @see attributeLabels() |
|
532
|
|
|
*/ |
|
533
|
54 |
|
public function getAttributeLabel($attribute) |
|
534
|
|
|
{ |
|
535
|
54 |
|
$labels = $this->attributeLabels(); |
|
536
|
54 |
|
return isset($labels[$attribute]) ? $labels[$attribute] : $this->generateAttributeLabel($attribute); |
|
537
|
|
|
} |
|
538
|
|
|
|
|
539
|
|
|
/** |
|
540
|
|
|
* Returns the text hint for the specified attribute. |
|
541
|
|
|
* @param string $attribute the attribute name |
|
542
|
|
|
* @return string the attribute hint |
|
543
|
|
|
* @see attributeHints() |
|
544
|
|
|
* @since 2.0.4 |
|
545
|
|
|
*/ |
|
546
|
16 |
|
public function getAttributeHint($attribute) |
|
547
|
|
|
{ |
|
548
|
16 |
|
$hints = $this->attributeHints(); |
|
549
|
16 |
|
return isset($hints[$attribute]) ? $hints[$attribute] : ''; |
|
550
|
|
|
} |
|
551
|
|
|
|
|
552
|
|
|
/** |
|
553
|
|
|
* Returns a value indicating whether there is any validation error. |
|
554
|
|
|
* @param string|null $attribute attribute name. Use null to check all attributes. |
|
555
|
|
|
* @return bool whether there is any error. |
|
556
|
|
|
*/ |
|
557
|
440 |
|
public function hasErrors($attribute = null) |
|
558
|
|
|
{ |
|
559
|
440 |
|
return $attribute === null ? !empty($this->_errors) : isset($this->_errors[$attribute]); |
|
560
|
|
|
} |
|
561
|
|
|
|
|
562
|
|
|
/** |
|
563
|
|
|
* Returns the errors for all attributes or a single attribute. |
|
564
|
|
|
* @param string|null $attribute attribute name. Use null to retrieve errors for all attributes. |
|
565
|
|
|
* @return array errors for all attributes or the specified attribute. Empty array is returned if no error. |
|
566
|
|
|
* See [[getErrors()]] for detailed description. |
|
567
|
|
|
* Note that when returning errors for all attributes, the result is a two-dimensional array, like the following: |
|
568
|
|
|
* |
|
569
|
|
|
* ```php |
|
570
|
|
|
* [ |
|
571
|
|
|
* 'username' => [ |
|
572
|
|
|
* 'Username is required.', |
|
573
|
|
|
* 'Username must contain only word characters.', |
|
574
|
|
|
* ], |
|
575
|
|
|
* 'email' => [ |
|
576
|
|
|
* 'Email address is invalid.', |
|
577
|
|
|
* ] |
|
578
|
|
|
* ] |
|
579
|
|
|
* ``` |
|
580
|
|
|
* |
|
581
|
|
|
* @see getFirstErrors() |
|
582
|
|
|
* @see getFirstError() |
|
583
|
|
|
*/ |
|
584
|
49 |
|
public function getErrors($attribute = null) |
|
585
|
|
|
{ |
|
586
|
49 |
|
if ($attribute === null) { |
|
587
|
9 |
|
return $this->_errors === null ? [] : $this->_errors; |
|
588
|
|
|
} |
|
589
|
|
|
|
|
590
|
41 |
|
return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : []; |
|
591
|
|
|
} |
|
592
|
|
|
|
|
593
|
|
|
/** |
|
594
|
|
|
* Returns the first error of every attribute in the model. |
|
595
|
|
|
* @return array the first errors. The array keys are the attribute names, and the array |
|
596
|
|
|
* values are the corresponding error messages. An empty array will be returned if there is no error. |
|
597
|
|
|
* @see getErrors() |
|
598
|
|
|
* @see getFirstError() |
|
599
|
|
|
*/ |
|
600
|
9 |
|
public function getFirstErrors() |
|
601
|
|
|
{ |
|
602
|
9 |
|
if (empty($this->_errors)) { |
|
603
|
4 |
|
return []; |
|
604
|
|
|
} |
|
605
|
|
|
|
|
606
|
6 |
|
$errors = []; |
|
607
|
6 |
|
foreach ($this->_errors as $name => $es) { |
|
608
|
6 |
|
if (!empty($es)) { |
|
609
|
6 |
|
$errors[$name] = reset($es); |
|
610
|
|
|
} |
|
611
|
|
|
} |
|
612
|
|
|
|
|
613
|
6 |
|
return $errors; |
|
614
|
|
|
} |
|
615
|
|
|
|
|
616
|
|
|
/** |
|
617
|
|
|
* Returns the first error of the specified attribute. |
|
618
|
|
|
* @param string $attribute attribute name. |
|
619
|
|
|
* @return string|null the error message. Null is returned if no error. |
|
620
|
|
|
* @see getErrors() |
|
621
|
|
|
* @see getFirstErrors() |
|
622
|
|
|
*/ |
|
623
|
42 |
|
public function getFirstError($attribute) |
|
624
|
|
|
{ |
|
625
|
42 |
|
return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null; |
|
626
|
|
|
} |
|
627
|
|
|
|
|
628
|
|
|
/** |
|
629
|
|
|
* Returns the errors for all attributes as a one-dimensional array. |
|
630
|
|
|
* @param bool $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise |
|
631
|
|
|
* only the first error message for each attribute will be shown. |
|
632
|
|
|
* @return array errors for all attributes as a one-dimensional array. Empty array is returned if no error. |
|
633
|
|
|
* @see getErrors() |
|
634
|
|
|
* @see getFirstErrors() |
|
635
|
|
|
* @since 2.0.14 |
|
636
|
|
|
*/ |
|
637
|
12 |
|
public function getErrorSummary($showAllErrors) |
|
638
|
|
|
{ |
|
639
|
12 |
|
$lines = []; |
|
640
|
12 |
|
$errors = $showAllErrors ? $this->getErrors() : $this->getFirstErrors(); |
|
641
|
12 |
|
foreach ($errors as $es) { |
|
642
|
9 |
|
$lines = array_merge($lines, (array)$es); |
|
643
|
|
|
} |
|
644
|
12 |
|
return $lines; |
|
645
|
|
|
} |
|
646
|
|
|
|
|
647
|
|
|
/** |
|
648
|
|
|
* Adds a new error to the specified attribute. |
|
649
|
|
|
* @param string $attribute attribute name |
|
650
|
|
|
* @param string $error new error message |
|
651
|
|
|
*/ |
|
652
|
156 |
|
public function addError($attribute, $error = '') |
|
653
|
|
|
{ |
|
654
|
156 |
|
$this->_errors[$attribute][] = $error; |
|
655
|
|
|
} |
|
656
|
|
|
|
|
657
|
|
|
/** |
|
658
|
|
|
* Adds a list of errors. |
|
659
|
|
|
* @param array $items a list of errors. The array keys must be attribute names. |
|
660
|
|
|
* The array values should be error messages. If an attribute has multiple errors, |
|
661
|
|
|
* these errors must be given in terms of an array. |
|
662
|
|
|
* You may use the result of [[getErrors()]] as the value for this parameter. |
|
663
|
|
|
* @since 2.0.2 |
|
664
|
|
|
*/ |
|
665
|
7 |
|
public function addErrors(array $items) |
|
666
|
|
|
{ |
|
667
|
7 |
|
foreach ($items as $attribute => $errors) { |
|
668
|
7 |
|
if (is_array($errors)) { |
|
669
|
7 |
|
foreach ($errors as $error) { |
|
670
|
7 |
|
$this->addError($attribute, $error); |
|
671
|
|
|
} |
|
672
|
|
|
} else { |
|
673
|
1 |
|
$this->addError($attribute, $errors); |
|
674
|
|
|
} |
|
675
|
|
|
} |
|
676
|
|
|
} |
|
677
|
|
|
|
|
678
|
|
|
/** |
|
679
|
|
|
* Removes errors for all attributes or a single attribute. |
|
680
|
|
|
* @param string|null $attribute attribute name. Use null to remove errors for all attributes. |
|
681
|
|
|
*/ |
|
682
|
137 |
|
public function clearErrors($attribute = null) |
|
683
|
|
|
{ |
|
684
|
137 |
|
if ($attribute === null) { |
|
685
|
133 |
|
$this->_errors = []; |
|
686
|
|
|
} else { |
|
687
|
5 |
|
unset($this->_errors[$attribute]); |
|
688
|
|
|
} |
|
689
|
|
|
} |
|
690
|
|
|
|
|
691
|
|
|
/** |
|
692
|
|
|
* Generates a user friendly attribute label based on the give attribute name. |
|
693
|
|
|
* This is done by replacing underscores, dashes and dots with blanks and |
|
694
|
|
|
* changing the first letter of each word to upper case. |
|
695
|
|
|
* For example, 'department_name' or 'DepartmentName' will generate 'Department Name'. |
|
696
|
|
|
* @param string $name the column name |
|
697
|
|
|
* @return string the attribute label |
|
698
|
|
|
*/ |
|
699
|
107 |
|
public function generateAttributeLabel($name) |
|
700
|
|
|
{ |
|
701
|
107 |
|
return Inflector::camel2words($name, true); |
|
702
|
|
|
} |
|
703
|
|
|
|
|
704
|
|
|
/** |
|
705
|
|
|
* Returns attribute values. |
|
706
|
|
|
* @param array|null $names list of attributes whose value needs to be returned. |
|
707
|
|
|
* Defaults to null, meaning all attributes listed in [[attributes()]] will be returned. |
|
708
|
|
|
* If it is an array, only the attributes in the array will be returned. |
|
709
|
|
|
* @param array $except list of attributes whose value should NOT be returned. |
|
710
|
|
|
* @return array attribute values (name => value). |
|
711
|
|
|
*/ |
|
712
|
17 |
|
public function getAttributes($names = null, $except = []) |
|
713
|
|
|
{ |
|
714
|
17 |
|
$values = []; |
|
715
|
17 |
|
if ($names === null) { |
|
716
|
17 |
|
$names = $this->attributes(); |
|
717
|
|
|
} |
|
718
|
17 |
|
foreach ($names as $name) { |
|
719
|
17 |
|
$values[$name] = $this->$name; |
|
720
|
|
|
} |
|
721
|
17 |
|
foreach ($except as $name) { |
|
722
|
1 |
|
unset($values[$name]); |
|
723
|
|
|
} |
|
724
|
|
|
|
|
725
|
17 |
|
return $values; |
|
726
|
|
|
} |
|
727
|
|
|
|
|
728
|
|
|
/** |
|
729
|
|
|
* Sets the attribute values in a massive way. |
|
730
|
|
|
* @param array $values attribute values (name => value) to be assigned to the model. |
|
731
|
|
|
* @param bool $safeOnly whether the assignments should only be done to the safe attributes. |
|
732
|
|
|
* A safe attribute is one that is associated with a validation rule in the current [[scenario]]. |
|
733
|
|
|
* @see safeAttributes() |
|
734
|
|
|
* @see attributes() |
|
735
|
|
|
*/ |
|
736
|
14 |
|
public function setAttributes($values, $safeOnly = true) |
|
737
|
|
|
{ |
|
738
|
14 |
|
if (is_array($values)) { |
|
|
|
|
|
|
739
|
14 |
|
$attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes()); |
|
740
|
14 |
|
foreach ($values as $name => $value) { |
|
741
|
14 |
|
if (isset($attributes[$name])) { |
|
742
|
14 |
|
$this->$name = $value; |
|
743
|
3 |
|
} elseif ($safeOnly) { |
|
744
|
3 |
|
$this->onUnsafeAttribute($name, $value); |
|
745
|
|
|
} |
|
746
|
|
|
} |
|
747
|
|
|
} |
|
748
|
|
|
} |
|
749
|
|
|
|
|
750
|
|
|
/** |
|
751
|
|
|
* This method is invoked when an unsafe attribute is being massively assigned. |
|
752
|
|
|
* The default implementation will log a warning message if YII_DEBUG is on. |
|
753
|
|
|
* It does nothing otherwise. |
|
754
|
|
|
* @param string $name the unsafe attribute name |
|
755
|
|
|
* @param mixed $value the attribute value |
|
756
|
|
|
*/ |
|
757
|
3 |
|
public function onUnsafeAttribute($name, $value) |
|
|
|
|
|
|
758
|
|
|
{ |
|
759
|
3 |
|
if (YII_DEBUG) { |
|
760
|
3 |
|
Yii::debug("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __METHOD__); |
|
761
|
|
|
} |
|
762
|
|
|
} |
|
763
|
|
|
|
|
764
|
|
|
/** |
|
765
|
|
|
* Returns the scenario that this model is used in. |
|
766
|
|
|
* |
|
767
|
|
|
* Scenario affects how validation is performed and which attributes can |
|
768
|
|
|
* be massively assigned. |
|
769
|
|
|
* |
|
770
|
|
|
* @return string the scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]]. |
|
771
|
|
|
*/ |
|
772
|
257 |
|
public function getScenario() |
|
773
|
|
|
{ |
|
774
|
257 |
|
return $this->_scenario; |
|
775
|
|
|
} |
|
776
|
|
|
|
|
777
|
|
|
/** |
|
778
|
|
|
* Sets the scenario for the model. |
|
779
|
|
|
* Note that this method does not check if the scenario exists or not. |
|
780
|
|
|
* The method [[validate()]] will perform this check. |
|
781
|
|
|
* @param string $value the scenario that this model is in. |
|
782
|
|
|
*/ |
|
783
|
11 |
|
public function setScenario($value) |
|
784
|
|
|
{ |
|
785
|
11 |
|
$this->_scenario = $value; |
|
786
|
|
|
} |
|
787
|
|
|
|
|
788
|
|
|
/** |
|
789
|
|
|
* Returns the attribute names that are safe to be massively assigned in the current scenario. |
|
790
|
|
|
* |
|
791
|
|
|
* @return string[] safe attribute names |
|
792
|
|
|
*/ |
|
793
|
33 |
|
public function safeAttributes() |
|
794
|
|
|
{ |
|
795
|
33 |
|
$scenario = $this->getScenario(); |
|
796
|
33 |
|
$scenarios = $this->scenarios(); |
|
797
|
33 |
|
if (!isset($scenarios[$scenario])) { |
|
798
|
3 |
|
return []; |
|
799
|
|
|
} |
|
800
|
33 |
|
$attributes = []; |
|
801
|
33 |
|
foreach ($scenarios[$scenario] as $attribute) { |
|
802
|
|
|
if ( |
|
803
|
33 |
|
$attribute !== '' |
|
804
|
33 |
|
&& strncmp($attribute, '!', 1) !== 0 |
|
805
|
33 |
|
&& !in_array('!' . $attribute, $scenarios[$scenario]) |
|
806
|
|
|
) { |
|
807
|
32 |
|
$attributes[] = $attribute; |
|
808
|
|
|
} |
|
809
|
|
|
} |
|
810
|
|
|
|
|
811
|
33 |
|
return $attributes; |
|
812
|
|
|
} |
|
813
|
|
|
|
|
814
|
|
|
/** |
|
815
|
|
|
* Returns the attribute names that are subject to validation in the current scenario. |
|
816
|
|
|
* @return string[] safe attribute names |
|
817
|
|
|
*/ |
|
818
|
158 |
|
public function activeAttributes() |
|
819
|
|
|
{ |
|
820
|
158 |
|
$scenario = $this->getScenario(); |
|
821
|
158 |
|
$scenarios = $this->scenarios(); |
|
822
|
158 |
|
if (!isset($scenarios[$scenario])) { |
|
823
|
3 |
|
return []; |
|
824
|
|
|
} |
|
825
|
158 |
|
$attributes = array_keys(array_flip($scenarios[$scenario])); |
|
826
|
158 |
|
foreach ($attributes as $i => $attribute) { |
|
827
|
83 |
|
if (strncmp($attribute, '!', 1) === 0) { |
|
828
|
5 |
|
$attributes[$i] = substr($attribute, 1); |
|
829
|
|
|
} |
|
830
|
|
|
} |
|
831
|
|
|
|
|
832
|
158 |
|
return $attributes; |
|
833
|
|
|
} |
|
834
|
|
|
|
|
835
|
|
|
/** |
|
836
|
|
|
* Populates the model with input data. |
|
837
|
|
|
* |
|
838
|
|
|
* This method provides a convenient shortcut for: |
|
839
|
|
|
* |
|
840
|
|
|
* ```php |
|
841
|
|
|
* if (isset($_POST['FormName'])) { |
|
842
|
|
|
* $model->attributes = $_POST['FormName']; |
|
843
|
|
|
* if ($model->save()) { |
|
844
|
|
|
* // handle success |
|
845
|
|
|
* } |
|
846
|
|
|
* } |
|
847
|
|
|
* ``` |
|
848
|
|
|
* |
|
849
|
|
|
* which, with `load()` can be written as: |
|
850
|
|
|
* |
|
851
|
|
|
* ```php |
|
852
|
|
|
* if ($model->load($_POST) && $model->save()) { |
|
853
|
|
|
* // handle success |
|
854
|
|
|
* } |
|
855
|
|
|
* ``` |
|
856
|
|
|
* |
|
857
|
|
|
* `load()` gets the `'FormName'` from the model's [[formName()]] method (which you may override), unless the |
|
858
|
|
|
* `$formName` parameter is given. If the form name is empty, `load()` populates the model with the whole of `$data`, |
|
859
|
|
|
* instead of `$data['FormName']`. |
|
860
|
|
|
* |
|
861
|
|
|
* Note, that the data being populated is subject to the safety check by [[setAttributes()]]. |
|
862
|
|
|
* |
|
863
|
|
|
* @param array $data the data array to load, typically `$_POST` or `$_GET`. |
|
864
|
|
|
* @param string|null $formName the form name to use to load the data into the model, empty string when form not use. |
|
865
|
|
|
* If not set, [[formName()]] is used. |
|
866
|
|
|
* @return bool whether `load()` found the expected form in `$data`. |
|
867
|
|
|
*/ |
|
868
|
4 |
|
public function load($data, $formName = null) |
|
869
|
|
|
{ |
|
870
|
4 |
|
$scope = $formName === null ? $this->formName() : $formName; |
|
871
|
4 |
|
if ($scope === '' && !empty($data)) { |
|
872
|
3 |
|
$this->setAttributes($data); |
|
873
|
|
|
|
|
874
|
3 |
|
return true; |
|
875
|
3 |
|
} elseif (isset($data[$scope])) { |
|
876
|
2 |
|
$this->setAttributes($data[$scope]); |
|
877
|
|
|
|
|
878
|
2 |
|
return true; |
|
879
|
|
|
} |
|
880
|
|
|
|
|
881
|
3 |
|
return false; |
|
882
|
|
|
} |
|
883
|
|
|
|
|
884
|
|
|
/** |
|
885
|
|
|
* Populates a set of models with the data from end user. |
|
886
|
|
|
* This method is mainly used to collect tabular data input. |
|
887
|
|
|
* The data to be loaded for each model is `$data[formName][index]`, where `formName` |
|
888
|
|
|
* refers to the value of [[formName()]], and `index` the index of the model in the `$models` array. |
|
889
|
|
|
* If [[formName()]] is empty, `$data[index]` will be used to populate each model. |
|
890
|
|
|
* The data being populated to each model is subject to the safety check by [[setAttributes()]]. |
|
891
|
|
|
* @param array $models the models to be populated. Note that all models should have the same class. |
|
892
|
|
|
* @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array |
|
893
|
|
|
* supplied by end user. |
|
894
|
|
|
* @param string|null $formName the form name to be used for loading the data into the models. |
|
895
|
|
|
* If not set, it will use the [[formName()]] value of the first model in `$models`. |
|
896
|
|
|
* This parameter is available since version 2.0.1. |
|
897
|
|
|
* @return bool whether at least one of the models is successfully populated. |
|
898
|
|
|
*/ |
|
899
|
1 |
|
public static function loadMultiple($models, $data, $formName = null) |
|
900
|
|
|
{ |
|
901
|
1 |
|
if ($formName === null) { |
|
902
|
|
|
/* @var $first Model|false */ |
|
903
|
1 |
|
$first = reset($models); |
|
904
|
1 |
|
if ($first === false) { |
|
905
|
|
|
return false; |
|
906
|
|
|
} |
|
907
|
1 |
|
$formName = $first->formName(); |
|
908
|
|
|
} |
|
909
|
|
|
|
|
910
|
1 |
|
$success = false; |
|
911
|
1 |
|
foreach ($models as $i => $model) { |
|
912
|
|
|
/* @var $model Model */ |
|
913
|
1 |
|
if ($formName == '') { |
|
914
|
1 |
|
if (!empty($data[$i]) && $model->load($data[$i], '')) { |
|
915
|
1 |
|
$success = true; |
|
916
|
|
|
} |
|
917
|
1 |
|
} elseif (!empty($data[$formName][$i]) && $model->load($data[$formName][$i], '')) { |
|
918
|
1 |
|
$success = true; |
|
919
|
|
|
} |
|
920
|
|
|
} |
|
921
|
|
|
|
|
922
|
1 |
|
return $success; |
|
923
|
|
|
} |
|
924
|
|
|
|
|
925
|
|
|
/** |
|
926
|
|
|
* Validates multiple models. |
|
927
|
|
|
* This method will validate every model. The models being validated may |
|
928
|
|
|
* be of the same or different types. |
|
929
|
|
|
* @param array $models the models to be validated |
|
930
|
|
|
* @param array|null $attributeNames list of attribute names that should be validated. |
|
931
|
|
|
* If this parameter is empty, it means any attribute listed in the applicable |
|
932
|
|
|
* validation rules should be validated. |
|
933
|
|
|
* @return bool whether all models are valid. False will be returned if one |
|
934
|
|
|
* or multiple models have validation error. |
|
935
|
|
|
*/ |
|
936
|
|
|
public static function validateMultiple($models, $attributeNames = null) |
|
937
|
|
|
{ |
|
938
|
|
|
$valid = true; |
|
939
|
|
|
/* @var $model Model */ |
|
940
|
|
|
foreach ($models as $model) { |
|
941
|
|
|
$valid = $model->validate($attributeNames) && $valid; |
|
942
|
|
|
} |
|
943
|
|
|
|
|
944
|
|
|
return $valid; |
|
945
|
|
|
} |
|
946
|
|
|
|
|
947
|
|
|
/** |
|
948
|
|
|
* Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified. |
|
949
|
|
|
* |
|
950
|
|
|
* A field is a named element in the returned array by [[toArray()]]. |
|
951
|
|
|
* |
|
952
|
|
|
* This method should return an array of field names or field definitions. |
|
953
|
|
|
* If the former, the field name will be treated as an object property name whose value will be used |
|
954
|
|
|
* as the field value. If the latter, the array key should be the field name while the array value should be |
|
955
|
|
|
* the corresponding field definition which can be either an object property name or a PHP callable |
|
956
|
|
|
* returning the corresponding field value. The signature of the callable should be: |
|
957
|
|
|
* |
|
958
|
|
|
* ```php |
|
959
|
|
|
* function ($model, $field) { |
|
960
|
|
|
* // return field value |
|
961
|
|
|
* } |
|
962
|
|
|
* ``` |
|
963
|
|
|
* |
|
964
|
|
|
* For example, the following code declares four fields: |
|
965
|
|
|
* |
|
966
|
|
|
* - `email`: the field name is the same as the property name `email`; |
|
967
|
|
|
* - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their |
|
968
|
|
|
* values are obtained from the `first_name` and `last_name` properties; |
|
969
|
|
|
* - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name` |
|
970
|
|
|
* and `last_name`. |
|
971
|
|
|
* |
|
972
|
|
|
* ```php |
|
973
|
|
|
* return [ |
|
974
|
|
|
* 'email', |
|
975
|
|
|
* 'firstName' => 'first_name', |
|
976
|
|
|
* 'lastName' => 'last_name', |
|
977
|
|
|
* 'fullName' => function ($model) { |
|
978
|
|
|
* return $model->first_name . ' ' . $model->last_name; |
|
979
|
|
|
* }, |
|
980
|
|
|
* ]; |
|
981
|
|
|
* ``` |
|
982
|
|
|
* |
|
983
|
|
|
* In this method, you may also want to return different lists of fields based on some context |
|
984
|
|
|
* information. For example, depending on [[scenario]] or the privilege of the current application user, |
|
985
|
|
|
* you may return different sets of visible fields or filter out some fields. |
|
986
|
|
|
* |
|
987
|
|
|
* The default implementation of this method returns [[attributes()]] indexed by the same attribute names. |
|
988
|
|
|
* |
|
989
|
|
|
* @return array the list of field names or field definitions. |
|
990
|
|
|
* @see toArray() |
|
991
|
|
|
*/ |
|
992
|
2 |
|
public function fields() |
|
993
|
|
|
{ |
|
994
|
2 |
|
$fields = $this->attributes(); |
|
995
|
|
|
|
|
996
|
2 |
|
return array_combine($fields, $fields); |
|
997
|
|
|
} |
|
998
|
|
|
|
|
999
|
|
|
/** |
|
1000
|
|
|
* Returns an iterator for traversing the attributes in the model. |
|
1001
|
|
|
* This method is required by the interface [[\IteratorAggregate]]. |
|
1002
|
|
|
* @return ArrayIterator an iterator for traversing the items in the list. |
|
1003
|
|
|
*/ |
|
1004
|
4 |
|
#[\ReturnTypeWillChange] |
|
1005
|
|
|
public function getIterator() |
|
1006
|
|
|
{ |
|
1007
|
4 |
|
$attributes = $this->getAttributes(); |
|
1008
|
4 |
|
return new ArrayIterator($attributes); |
|
1009
|
|
|
} |
|
1010
|
|
|
|
|
1011
|
|
|
/** |
|
1012
|
|
|
* Returns whether there is an element at the specified offset. |
|
1013
|
|
|
* This method is required by the SPL interface [[\ArrayAccess]]. |
|
1014
|
|
|
* It is implicitly called when you use something like `isset($model[$offset])`. |
|
1015
|
|
|
* @param string $offset the offset to check on. |
|
1016
|
|
|
* @return bool whether or not an offset exists. |
|
1017
|
|
|
*/ |
|
1018
|
2 |
|
#[\ReturnTypeWillChange] |
|
1019
|
|
|
public function offsetExists($offset) |
|
1020
|
|
|
{ |
|
1021
|
2 |
|
return isset($this->$offset); |
|
1022
|
|
|
} |
|
1023
|
|
|
|
|
1024
|
|
|
/** |
|
1025
|
|
|
* Returns the element at the specified offset. |
|
1026
|
|
|
* This method is required by the SPL interface [[\ArrayAccess]]. |
|
1027
|
|
|
* It is implicitly called when you use something like `$value = $model[$offset];`. |
|
1028
|
|
|
* @param string $offset the offset to retrieve element. |
|
1029
|
|
|
* @return mixed the element at the offset, null if no element is found at the offset |
|
1030
|
|
|
*/ |
|
1031
|
254 |
|
#[\ReturnTypeWillChange] |
|
1032
|
|
|
public function offsetGet($offset) |
|
1033
|
|
|
{ |
|
1034
|
254 |
|
return $this->$offset; |
|
1035
|
|
|
} |
|
1036
|
|
|
|
|
1037
|
|
|
/** |
|
1038
|
|
|
* Sets the element at the specified offset. |
|
1039
|
|
|
* This method is required by the SPL interface [[\ArrayAccess]]. |
|
1040
|
|
|
* It is implicitly called when you use something like `$model[$offset] = $value;`. |
|
1041
|
|
|
* @param string $offset the offset to set element |
|
1042
|
|
|
* @param mixed $value the element value |
|
1043
|
|
|
*/ |
|
1044
|
4 |
|
#[\ReturnTypeWillChange] |
|
1045
|
|
|
public function offsetSet($offset, $value) |
|
1046
|
|
|
{ |
|
1047
|
4 |
|
$this->$offset = $value; |
|
1048
|
|
|
} |
|
1049
|
|
|
|
|
1050
|
|
|
/** |
|
1051
|
|
|
* Sets the element value at the specified offset to null. |
|
1052
|
|
|
* This method is required by the SPL interface [[\ArrayAccess]]. |
|
1053
|
|
|
* It is implicitly called when you use something like `unset($model[$offset])`. |
|
1054
|
|
|
* @param string $offset the offset to unset element |
|
1055
|
|
|
*/ |
|
1056
|
1 |
|
#[\ReturnTypeWillChange] |
|
1057
|
|
|
public function offsetUnset($offset) |
|
1058
|
|
|
{ |
|
1059
|
1 |
|
$this->$offset = null; |
|
1060
|
|
|
} |
|
1061
|
|
|
|
|
1062
|
|
|
/** |
|
1063
|
|
|
* {@inheritdoc} |
|
1064
|
|
|
*/ |
|
1065
|
4 |
|
public function __clone() |
|
1066
|
|
|
{ |
|
1067
|
4 |
|
parent::__clone(); |
|
1068
|
|
|
|
|
1069
|
4 |
|
$this->_errors = null; |
|
1070
|
4 |
|
$this->_validators = null; |
|
1071
|
|
|
} |
|
1072
|
|
|
} |
|
1073
|
|
|
|