今天继续了解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 }