yii2源码学习笔记(七)

今天继续了解model类

1   /**
  2      * Returns the form name that this model class should use.
  3      *
  4      * 返回表单的名称,就是这个 model 的类名
  5      *
  6      * The form name is mainly used by [[yiiwidgetsActiveForm]] to determine how to name
  7      * the input fields for the attributes in a model. If the form name is "A" and an attribute
  8      * name is "b", then the corresponding input name would be "A[b]". If the form name is
  9      * an empty string, then the input name would be "b".
 10      *
 11      * By default, this method returns the model class name (without the namespace part)
 12      * as the form name. You may override it when the model is used in different forms.
 13      *
 14      * @return string the form name of this model class.
 15      */
 16     public function formName()
 17     {
 18         // ReflectionClass 类包含了一个类的有关信息
 19         $reflector = new ReflectionClass($this);
 20         // 获取类的短名,就是不含命名空间(namespace)的那一部分
 21         return $reflector->getShortName();
 22     }
 23 
 24     /**
 25      * Returns the list of attribute names.
 26      * 返回属性名的列表,注意:只会返回 public 且不是 static 的属性
 27      * By default, this method returns all public non-static properties of the class.
 28      * You may override this method to change the default behavior.
 29      * @return array list of attribute names.
 30      */
 31     public function attributes()
 32     {
 33         $class = new ReflectionClass($this);
 34         $names = [];
 35         // ReflectionClass::getProperties — 获取一组属性
 36         // ReflectionProperty::IS_STATIC 指示了 static 的属性。
 37         // ReflectionProperty::IS_PUBLIC 指示了 public 的属性。
 38         // ReflectionProperty::IS_PROTECTED 指示了 protected 的属性。
 39         // ReflectionProperty::IS_PRIVATE 指示了 private 的属性。
 40         foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
 41             // 如果是public的属性,并且不是static的,就认为是它的attribute
 42             if (!$property->isStatic()) {
 43                 // 获取该属性的名称
 44                 $names[] = $property->getName();
 45             }
 46         }
 47 
 48         return $names;
 49     }
 50 
 51     /**
 52      * Returns the attribute labels.
 53      * 返回属性的标签
 54      *
 55      * Attribute labels are mainly used for display purpose. For example, given an attribute
 56      * `firstName`, we can declare a label `First Name` which is more user-friendly and can
 57      * be displayed to end users.
 58      *
 59      * By default an attribute label is generated using [[generateAttributeLabel()]].
 60      * This method allows you to explicitly specify attribute labels.
 61      *
 62      * Note, in order to inherit labels defined in the parent class, a child class needs to
 63      * merge the parent labels with child labels using functions such as `array_merge()`.
 64      *
 65      * @return array attribute labels (name => label)
 66      * @see generateAttributeLabel()
 67      */
 68     public function attributeLabels()
 69     {
 70         return [];
 71     }
 72 
 73     /**
 74      * Performs the data validation.
 75      *
 76      * This method executes the validation rules applicable to the current [[scenario]].
 77      * The following criteria are used to determine whether a rule is currently applicable:
 78      *
 79      * - the rule must be associated with the attributes relevant to the current scenario;
 80      * - the rules must be effective for the current scenario.
 81      *
 82      * This method will call [[beforeValidate()]] and [[afterValidate()]] before and
 83      * after the actual validation, respectively. If [[beforeValidate()]] returns false,
 84      * the validation will be cancelled and [[afterValidate()]] will not be called.
 85      *
 86      * Errors found during the validation can be retrieved via [[getErrors()]],
 87      * [[getFirstErrors()]] and [[getFirstError()]].
 88      *
 89      * @param array $attributeNames list of attribute names that should be validated.
 90      * If this parameter is empty, it means any attribute listed in the applicable
 91      * validation rules should be validated.
 92      * @param boolean $clearErrors whether to call [[clearErrors()]] before performing validation
 93      * @return boolean whether the validation is successful without any error.
 94      * @throws InvalidParamException if the current scenario is unknown.
 95      */
 96     public function validate($attributeNames = null, $clearErrors = true)
 97     {
 98         if ($clearErrors) {
 99             $this->clearErrors();
100         }
101 
102         if (!$this->beforeValidate()) {
103             return false;
104         }
105 
106         $scenarios = $this->scenarios();
107         $scenario = $this->getScenario();
108         if (!isset($scenarios[$scenario])) {
109             throw new InvalidParamException("Unknown scenario: $scenario");
110         }
111 
112         if ($attributeNames === null) {
113             $attributeNames = $this->activeAttributes();
114         }
115 
116         foreach ($this->getActiveValidators() as $validator) {
117             $validator->validateAttributes($this, $attributeNames);
118         }
119         $this->afterValidate();
120 
121         return !$this->hasErrors();
122     }
123 
124     /**
125      * This method is invoked before validation starts.
126      * The default implementation raises a `beforeValidate` event.
127      * You may override this method to do preliminary checks before validation.
128      * Make sure the parent implementation is invoked so that the event can be raised.
129      * @return boolean whether the validation should be executed. Defaults to true.
130      * If false is returned, the validation will stop and the model is considered invalid.
131      */
132     public function beforeValidate()
133     {
134         $event = new ModelEvent;
135         $this->trigger(self::EVENT_BEFORE_VALIDATE, $event);
136 
137         return $event->isValid;
138     }
139 
140     /**
141      * This method is invoked after validation ends.
142      * The default implementation raises an `afterValidate` event.
143      * You may override this method to do postprocessing after validation.
144      * Make sure the parent implementation is invoked so that the event can be raised.
145      */
146     public function afterValidate()
147     {
148         $this->trigger(self::EVENT_AFTER_VALIDATE);
149     }
150 
151     /**
152      * Returns all the validators declared in [[rules()]].
153      *
154      * This method differs from [[getActiveValidators()]] in that the latter
155      * only returns the validators applicable to the current [[scenario]].
156      *
157      * Because this method returns an ArrayObject object, you may
158      * manipulate it by inserting or removing validators (useful in model behaviors).
159      * For example,
160      *
161      * ~~~
162      * $model->validators[] = $newValidator;
163      * ~~~
164      *
165      * @return ArrayObject|yiivalidatorsValidator[] all the validators declared in the model.
166      */
167     public function getValidators()
168     {
169         if ($this->_validators === null) {
170             $this->_validators = $this->createValidators();
171         }
172         return $this->_validators;
173     }
174 
175     /**
176      * Returns the validators applicable to the current [[scenario]].
177      * @param string $attribute the name of the attribute whose applicable validators should be returned.
178      * If this is null, the validators for ALL attributes in the model will be returned.
179      * @return yiivalidatorsValidator[] the validators applicable to the current [[scenario]].
180      */
181     public function getActiveValidators($attribute = null)
182     {
183         $validators = [];
184         $scenario = $this->getScenario();
185         foreach ($this->getValidators() as $validator) {
186             if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) {
187                 $validators[] = $validator;
188             }
189         }
190         return $validators;
191     }
192 
193     /**
194      * Creates validator objects based on the validation rules specified in [[rules()]].
195      * Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned.
196      * @return ArrayObject validators
197      * @throws InvalidConfigException if any validation rule configuration is invalid
198      */
199     public function createValidators()
200     {
201         $validators = new ArrayObject;
202         foreach ($this->rules() as $rule) {
203             if ($rule instanceof Validator) {
204                 $validators->append($rule);
205             } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
206                 $validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2));
207                 $validators->append($validator);
208             } else {
209                 throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
210             }
211         }
212         return $validators;
213     }
214 
215     /**
216      * Returns a value indicating whether the attribute is required.
217      * This is determined by checking if the attribute is associated with a
218      * [[yiivalidatorsRequiredValidator|required]] validation rule in the
219      * current [[scenario]].
220      *
221      * Note that when the validator has a conditional validation applied using
222      * [[yiivalidatorsRequiredValidator::$when|$when]] this method will return
223      * `false` regardless of the `when` condition because it may be called be
224      * before the model is loaded with data.
225      *
226      * @param string $attribute attribute name
227      * @return boolean whether the attribute is required
228      */
229     public function isAttributeRequired($attribute)
230     {
231         foreach ($this->getActiveValidators($attribute) as $validator) {
232             if ($validator instanceof RequiredValidator && $validator->when === null) {
233                 return true;
234             }
235         }
236         return false;
237     }
238 
239     /**
240      * Returns a value indicating whether the attribute is safe for massive assignments.
241      * @param string $attribute attribute name
242      * @return boolean whether the attribute is safe for massive assignments
243      * @see safeAttributes()
244      */
245     public function isAttributeSafe($attribute)
246     {
247         return in_array($attribute, $this->safeAttributes(), true);
248     }
249 
250     /**
251      * Returns a value indicating whether the attribute is active in the current scenario.
252      * @param string $attribute attribute name
253      * @return boolean whether the attribute is active in the current scenario
254      * @see activeAttributes()
255      */
256     public function isAttributeActive($attribute)
257     {
258         return in_array($attribute, $this->activeAttributes(), true);
259     }
260 
261     /**
262      * Returns the text label for the specified attribute.
263      * @param string $attribute the attribute name
264      * @return string the attribute label
265      * @see generateAttributeLabel()
266      * @see attributeLabels()
267      */
268     public function getAttributeLabel($attribute)
269     {
270         $labels = $this->attributeLabels();
271         return isset($labels[$attribute]) ? $labels[$attribute] : $this->generateAttributeLabel($attribute);
272     }
273 
274     /**
275      * Returns a value indicating whether there is any validation error.
276      * @param string|null $attribute attribute name. Use null to check all attributes.
277      * @return boolean whether there is any error.
278      */
279     public function hasErrors($attribute = null)
280     {
281         return $attribute === null ? !empty($this->_errors) : isset($this->_errors[$attribute]);
282     }
283 
284     /**
285      * Returns the errors for all attribute or a single attribute.
286      * @param string $attribute attribute name. Use null to retrieve errors for all attributes.
287      * @property array An array of errors for all attributes. Empty array is returned if no error.
288      * The result is a two-dimensional array. See [[getErrors()]] for detailed description.
289      * @return array errors for all attributes or the specified attribute. Empty array is returned if no error.
290      * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following:
291      *
292      * ~~~
293      * [
294      *     'username' => [
295      *         'Username is required.',
296      *         'Username must contain only word characters.',
297      *     ],
298      *     'email' => [
299      *         'Email address is invalid.',
300      *     ]
301      * ]
302      * ~~~
303      *
304      * @see getFirstErrors()
305      * @see getFirstError()
306      */
307     public function getErrors($attribute = null)
308     {
309         if ($attribute === null) {
310             return $this->_errors === null ? [] : $this->_errors;
311         } else {
312             return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : [];
313         }
314     }
315 
316     /**
317      * Returns the first error of every attribute in the model.
318      * @return array the first errors. The array keys are the attribute names, and the array
319      * values are the corresponding error messages. An empty array will be returned if there is no error.
320      * @see getErrors()
321      * @see getFirstError()
322      */
323     public function getFirstErrors()
324     {
325         if (empty($this->_errors)) {
326             return [];
327         } else {
328             $errors = [];
329             foreach ($this->_errors as $name => $es) {
330                 if (!empty($es)) {
331                     $errors[$name] = reset($es);
332                 }
333             }
334 
335             return $errors;
336         }
337     }
338 
339     /**
340      * Returns the first error of the specified attribute.
341      * @param string $attribute attribute name.
342      * @return string the error message. Null is returned if no error.
343      * @see getErrors()
344      * @see getFirstErrors()
345      */
346     public function getFirstError($attribute)
347     {
348         return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null;
349     }
350 
351     /**
352      * Adds a new error to the specified attribute.
353      * @param string $attribute attribute name
354      * @param string $error new error message
355      */
356     public function addError($attribute, $error = '')
357     {
358         $this->_errors[$attribute][] = $error;
359     }
360 
361     /**
362      * Adds a list of errors.
363      * @param array $items a list of errors. The array keys must be attribute names.
364      * The array values should be error messages. If an attribute has multiple errors,
365      * these errors must be given in terms of an array.
366      * You may use the result of [[getErrors()]] as the value for this parameter.
367      * @since 2.0.2
368      */
369     public function addErrors(array $items)
370     {
371         foreach ($items as $attribute => $errors) {
372             if (is_array($errors)) {
373                 foreach ($errors as $error) {
374                     $this->addError($attribute, $error);
375                 }
376             } else {
377                 $this->addError($attribute, $errors);
378             }
379         }
380     }
381 
382     /**
383      * Removes errors for all attributes or a single attribute.
384      * @param string $attribute attribute name. Use null to remove errors for all attribute.
385      */
386     public function clearErrors($attribute = null)
387     {
388         if ($attribute === null) {
389             $this->_errors = [];
390         } else {
391             unset($this->_errors[$attribute]);
392         }
393     }
394 
395     /**
396      * Generates a user friendly attribute label based on the give attribute name.
397      * This is done by replacing underscores, dashes and dots with blanks and
398      * changing the first letter of each word to upper case.
399      * For example, 'department_name' or 'DepartmentName' will generate 'Department Name'.
400      * @param string $name the column name
401      * @return string the attribute label
402      */
403     public function generateAttributeLabel($name)
404     {
405         return Inflector::camel2words($name, true);
406     }
407 
408     /**
409      * Returns attribute values.
410      * @param array $names list of attributes whose value needs to be returned.
411      * Defaults to null, meaning all attributes listed in [[attributes()]] will be returned.
412      * If it is an array, only the attributes in the array will be returned.
413      * @param array $except list of attributes whose value should NOT be returned.
414      * @return array attribute values (name => value).
415      */
416     public function getAttributes($names = null, $except = [])
417     {
418         $values = [];
419         if ($names === null) {
420             $names = $this->attributes();
421         }
422         foreach ($names as $name) {
423             $values[$name] = $this->$name;
424         }
425         foreach ($except as $name) {
426             unset($values[$name]);
427         }
428 
429         return $values;
430     }
431 
432     /**
433      * Sets the attribute values in a massive way.
434      * @param array $values attribute values (name => value) to be assigned to the model.
435      * @param boolean $safeOnly whether the assignments should only be done to the safe attributes.
436      * A safe attribute is one that is associated with a validation rule in the current [[scenario]].
437      * @see safeAttributes()
438      * @see attributes()
439      */
440     public function setAttributes($values, $safeOnly = true)
441     {
442         // 必须是个数组
443         if (is_array($values)) {
444             // array_flip — 交换数组中的键和值
445             // 将属性放到了 key 上
446             // 默认取 safeAttributes 中的属性
447             $attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes());
448             foreach ($values as $name => $value) {
449                 if (isset($attributes[$name])) {
450                     // 如果存在该属性,就直接赋值
451                     $this->$name = $value;
452                 } elseif ($safeOnly) {
453                     // 如果不存在,而且是 safeOnly 的话,就触发一下 onUnsafeAttribute 方法
454                     $this->onUnsafeAttribute($name, $value);
455                 }
456             }
457         }
458     }
459 
460     /**
461      * This method is invoked when an unsafe attribute is being massively assigned.
462      * The default implementation will log a warning message if YII_DEBUG is on.
463      * It does nothing otherwise.
464      * @param string $name the unsafe attribute name
465      * @param mixed $value the attribute value
466      */
467     public function onUnsafeAttribute($name, $value)
468     {
469         if (YII_DEBUG) {
470             // 如果是调试状态,就打 log 记录下,没有成功设置的不安全的属性
471             Yii::trace("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __METHOD__);
472         }
473     }
474 
475     /**
476      * Returns the scenario that this model is used in.
477      *
478      * Scenario affects how validation is performed and which attributes can
479      * be massively assigned.
480      *
481      * @return string the scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]].
482      */
483     public function getScenario()
484     {
485         // 获取当前的场景
486         return $this->_scenario;
487     }
488 
489     /**
490      * Sets the scenario for the model.
491      * Note that this method does not check if the scenario exists or not.
492      * The method [[validate()]] will perform this check.
493      * @param string $value the scenario that this model is in.
494      */
495     public function setScenario($value)
496     {
497         // 设置当前的场景
498         $this->_scenario = $value;
499     }
500 
501     /**
502      * Returns the attribute names that are safe to be massively assigned in the current scenario.
503      * @return string[] safe attribute names
504      */
505     public function safeAttributes()
506     {
507         // 获取当前的场景
508         $scenario = $this->getScenario();
509         // 获取所有场景及其属性
510         $scenarios = $this->scenarios();
511         if (!isset($scenarios[$scenario])) {
512             // 场景不存在,就返回空
513             return [];
514         }
515         $attributes = [];
516         foreach ($scenarios[$scenario] as $attribute) {
517             // 将开头不是!的属性才会放入到 safeAttributes 中, 即以!开头的属性不会被放到 safeAttributes 中
518             if ($attribute[0] !== '!') {
519                 $attributes[] = $attribute;
520             }
521         }
522 
523         return $attributes;
524     }
525 
526     /**
527      * Returns the attribute names that are subject to validation in the current scenario.
528      * @return string[] safe attribute names
529      */
530     public function activeAttributes()
531     {
532         // 同上
533         $scenario = $this->getScenario();
534         $scenarios = $this->scenarios();
535         if (!isset($scenarios[$scenario])) {
536             return [];
537         }
538         // 获取当前场景中的所有属性
539         $attributes = $scenarios[$scenario];
540         foreach ($attributes as $i => $attribute) {
541             // 如果属性名以!开头,就把!截取掉
542             // !开头的属性来自rules,加!能够使规则(即 validator)生效,但却能够不出现在 safeAttributes 中
543             if ($attribute[0] === '!') {
544                 $attributes[$i] = substr($attribute, 1);
545             }
546         }
547 
548         return $attributes;
549     }
550 
551     /**
552      * Populates the model with the data from end user.
553      * The data to be loaded is `$data[formName]`, where `formName` refers to the value of [[formName()]].
554      * If [[formName()]] is empty, the whole `$data` array will be used to populate the model.
555      * The data being populated is subject to the safety check by [[setAttributes()]].
556      * 加载数据到所在的 model 中
557      * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array
558      * supplied by end user.
559      * @param string $formName the form name to be used for loading the data into the model.
560      * If not set, [[formName()]] will be used.
561      * @return boolean whether the model is successfully populated with some data.
562      */
563     public function load($data, $formName = null)
564     {
565         // 如果存在 yii 的 form,就使用该 form,否则就拿到所在类的名称(不含 namespace)
566         $scope = $formName === null ? $this->formName() : $formName;
567         if ($scope === '' && !empty($data)) {
568             // 如果 $scope 为空字符串,且 $data不为空,就设置属性
569             // 即 $formName 为空字符串,且 $data不为空
570             $this->setAttributes($data);
571 
572             return true;
573         } elseif (isset($data[$scope])) {
574             // 否则,必须存在 $data[$scope],使用 $data[$scope] 去设置属性
575             $this->setAttributes($data[$scope]);
576 
577             return true;
578         } else {
579             return false;
580         }
581     }
582 
583     /**
584      * Populates a set of models with the data from end user.
585      * 加载数据到所在的 model 的集合中
586      * This method is mainly used to collect tabular data input.
587      * The data to be loaded for each model is `$data[formName][index]`, where `formName`
588      * refers to the value of [[formName()]], and `index` the index of the model in the `$models` array.
589      * If [[formName()]] is empty, `$data[index]` will be used to populate each model.
590      * The data being populated to each model is subject to the safety check by [[setAttributes()]].
591      * @param array $models the models to be populated. Note that all models should have the same class.
592      * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array
593      * supplied by end user.
594      * @param string $formName the form name to be used for loading the data into the models.
595      * If not set, it will use the [[formName()]] value of the first model in `$models`.
596      * This parameter is available since version 2.0.1.
597      * @return boolean whether at least one of the models is successfully populated.
598      */
599     public static function loadMultiple($models, $data, $formName = null)
600     {
601         if ($formName === null) {
602             /* @var $first Model */
603             // reset — 将数组的内部指针指向第一个单元
604             $first = reset($models);
605             if ($first === false) {
606                 // 不存在就返回 false
607                 return false;
608             }
609             // 拿到所在类的名称(不含 namespace)
610             $formName = $first->formName();
611         }
612 
613         $success = false;
614         // 遍历 $models,一个个 load 数据
615         foreach ($models as $i => $model) {
616             /* @var $model Model */
617             if ($formName == '') {
618                 if (!empty($data[$i])) {
619                     // 数据不为空,就 load 到相应的 model 中
620                     $model->load($data[$i], '');
621                     $success = true;
622                 }
623             } elseif (!empty($data[$formName][$i])) {
624                 // 存在 $formName,且数据不为空,就 load 到相应的 model 中
625                 $model->load($data[$formName][$i], '');
626                 $success = true;
627             }
628         }
629 
630         return $success;
631     }
632 
633     /**
634      * Validates multiple models.
635      * This method will validate every model. The models being validated may
636      * be of the same or different types.
637      * @param array $models the models to be validated
638      * @param array $attributeNames list of attribute names that should be validated.
639      * If this parameter is empty, it means any attribute listed in the applicable
640      * validation rules should be validated.
641      * @return boolean whether all models are valid. False will be returned if one
642      * or multiple models have validation error.
643      */
644     public static function validateMultiple($models, $attributeNames = null)
645     {
646         $valid = true;
647         /* @var $model Model */
648         foreach ($models as $model) {
649             //遍历$models 调用validate()方法
650             $valid = $model->validate($attributeNames) && $valid;
651         }
652 
653         return $valid;
654     }
655 
656     /**
657      * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.
658      * 以数组形式返回定义的一个字段名或字段
659      * A field is a named element in the returned array by [[toArray()]].
660      * 
661      * This method should return an array of field names or field definitions.
662      *  此方法应该返回一个字段名或字段定义的数组
663      * If the former, the field name will be treated as an object property name whose value will be used
664      * as the field value. If the latter, the array key should be the field name while the array value should be
665      * the corresponding field definition which can be either an object property name or a PHP callable
666      * returning the corresponding field value. The signature of the callable should be:
667      * 如果前者,该字段名将被视为一个对象属性名,其值将用作该字段值。
668      * 如果是后者,数组的键应该是字段名称,数组的值应相应的字段定义可以是一个对象的属性名称或PHP回调函数
669      * ```php
670      * function ($field, $model) {
671      *     // return field value
672      * }
673      * ```
674      *
675      * For example, the following code declares four fields:
676      *
677      * - `email`: the field name is the same as the property name `email`;
678      * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their
679      *   values are obtained from the `first_name` and `last_name` properties;
680      * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`
681      *   and `last_name`.
682      *
683      * ```php
684      * return [
685      *     'email',
686      *     'firstName' => 'first_name',
687      *     'lastName' => 'last_name',
688      *     'fullName' => function ($model) {
689      *         return $model->first_name . ' ' . $model->last_name;
690      *     },
691      * ];
692      * ```
693      *
694      * In this method, you may also want to return different lists of fields based on some context
695      * information. For example, depending on [[scenario]] or the privilege of the current application user,
696      * you may return different sets of visible fields or filter out some fields.
697      * 在这个方法中,可能还希望在根据条件返回不同的字段列表,例如,根据[[scenario]]或者当前应用程序用户的权限
698      * 设置不同的可见字段或者过滤某些字段
699      * The default implementation of this method returns [[attributes()]] indexed by the same attribute names.
700      * 默认返回[[attributes()]]中的属性名为索引的所有字段
701      * @return array the list of field names or field definitions.
702      * @see toArray()
703      */
704     public function fields()
705     {
706         $fields = $this->attributes();
707         // array_combine — 创建一个数组,用一个数组的值作为其键名,另一个数组的值作为其值
708         return array_combine($fields, $fields);
709     }
710 
711     /**
712      * Returns an iterator for traversing the attributes in the model.
713      * This method is required by the interface IteratorAggregate.
714      * @return ArrayIterator an iterator for traversing the items in the list.
715      */
716     public function getIterator()
717     {
718         // 获取该 model 的所有属性
719         $attributes = $this->getAttributes();
720         // ArrayIterator 这个迭代器允许在遍历数组和对象时删除和更新值与键
721         // 当你想多次遍历相同数组时你需要实例化 ArrayObject,然后让这个实例创建一个 ArrayIteratror 实例, 然后使用 foreach 或者 手动调用 getIterator() 方法
722         return new ArrayIterator($attributes);
723     }
724 
725     /**
726      * Returns whether there is an element at the specified offset.
727      * This method is required by the SPL interface `ArrayAccess`.
728      * It is implicitly called when you use something like `isset($model[$offset])`.
729      * @param mixed $offset the offset to check on
730      * @return boolean
731      */
732     public function offsetExists($offset)
733     {
734         // 将 isset($model[$offset]) 重写为 isset($model->$offset)
735         return $this->$offset !== null;
736     }
737 
738     /**
739      * Returns the element at the specified offset.
740      * This method is required by the SPL interface `ArrayAccess`.
741      * It is implicitly called when you use something like `$value = $model[$offset];`.
742      * @param mixed $offset the offset to retrieve element.
743      * @return mixed the element at the offset, null if no element is found at the offset
744      */
745     public function offsetGet($offset)
746     {
747         // 将获取 $model[$offset] 重写为 $model->$offset
748         return $this->$offset;
749     }
750 
751     /**
752      * Sets the element at the specified offset.
753      * This method is required by the SPL interface `ArrayAccess`.
754      * It is implicitly called when you use something like `$model[$offset] = $item;`.
755      * @param integer $offset the offset to set element
756      * @param mixed $item the element value
757      */
758     public function offsetSet($offset, $item)
759     {
760         // 将 $model[$offset] = $item 重写为 $model->$offset = $item
761         $this->$offset = $item;
762     }
763 
764     /**
765      * Sets the element value at the specified offset to null.
766      * This method is required by the SPL interface `ArrayAccess`.
767      * It is implicitly called when you use something like `unset($model[$offset])`.
768      * @param mixed $offset the offset to unset element
769      */
770     public function offsetUnset($offset)
771     {
772         // 将 unset($model[$offset]) 重写为 $model->$offset = null
773         $this->$offset = null;
774     }
原文地址:https://www.cnblogs.com/dragon16/p/5535878.html