模型類是數據模型的基類.此類繼承了組件類,實現了3個接口
先介紹一下模型類前面的大量注釋說了什么: * 模型類是數據模型的基類.此類繼承了組件類,實現了3個接口 * 實現了IteratorAggregate(聚合式迭代器)接口,實現了ArrayAccess接口,可以像數組一樣訪問對象,這兩個接口是php自帶 * Arrayable接口是yii2框架自帶 * 模型實現了以下常用功能: * * - 屬性聲明: 默認情況下,每個公共類成員都被認為是模型屬性 * - 屬性標簽: 每個屬性可以與用於顯示目的的標簽相關聯。 * - 大量的屬性分配 * - 基於場景的驗證 * * 在執行數據驗證時,模型還引發下列事件: * * - [[EVENT_BEFORE_VALIDATE]]: 在開始時提出的事件 [[validate()]] * - [[EVENT_AFTER_VALIDATE]]: 結束時提出的事件[[validate()]] * * 您可以直接使用模型存儲模型數據, 或延長定制.
<?php /** * @property \yii\validators\Validator[] $activeValidators 使用場景效驗[[scenario]],此屬性是只讀的 * @property array $attributes 屬性值鍵值對方式 (name => value). * @property array $errors 所有屬性的錯誤數組. 數組為空表示沒有錯誤. 結果是一個二維數組 * [[getErrors()]]獲取詳細錯誤信息,此屬性是只讀的 * @property array $firstErrors 第一個錯誤,數組的鍵是屬性名, 數組的值是錯誤信息,空數組表示沒有錯誤,此屬性只讀 * @property ArrayIterator $iterator 遍歷列表中的項的迭代器,此屬性只讀. * @property string $scenario 模型所在的場景.默認是[[SCENARIO_DEFAULT]]. * @property ArrayObject|\yii\validators\Validator[] $validators 在模型中定義的所有方法,此屬性只讀. * * @author Qiang Xue <qiang.xue@gmail.com> * @since 2.0 */ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayable { //自 PHP 5.4.0 起,PHP 實現了代碼復用的一個方法,稱為 traits,此處就使用了trait use ArrayableTrait; /** * 默認場景名 */ const SCENARIO_DEFAULT = 'default'; /** * @event 模型事件先被調用驗證,調用[[validate()]]這個方法. You may set * 可以設置[[ModelEvent::isValid]] 的isValid屬性為false來阻止驗證. */ const EVENT_BEFORE_VALIDATE = 'beforeValidate'; /** * @event 驗證[[validate()]]后執行的事件 */ const EVENT_AFTER_VALIDATE = 'afterValidate'; /** * @var array 存放驗證錯誤的數組,鍵是屬性名,值是有關錯誤的數組 (attribute name => array of errors) */ private $_errors; /** * @var ArrayObject list 驗證器集合 */ private $_validators; /** * @var string 當前場景 */ private $_scenario = self::SCENARIO_DEFAULT; /** * 返回屬性的驗證規則. * * 驗證規則通過 [[validate()]] 方法檢驗屬性是否是有效的 * 子類應該覆蓋這個方法來聲明不同的驗證規則 * * 每個驗證規則都是下列結構的數組: * * ```php * [ * ['attribute1', 'attribute2'], * 'validator type', * 'on' => ['scenario1', 'scenario2'], * //...other parameters... * ] * ``` * * where * * - 屬性集合: 必選, 指定要驗證的屬性數組, 對於單個屬性,你可以直接傳遞字符串; * - 驗證類型: 必選, 指定要使用的驗證. 它可以是一個內置驗證器的名字, * 模型類的方法名稱, 匿名函數, 或驗證器類的名稱. * - on: 可選參數, 是一個數組,表示在指定場景使用,沒設置,表示應用於所有的場景 * - 額外的名稱-值對可以指定初始化相應的驗證特性. * * 一個驗證器可以是一個類的對象延伸擴展[[Validator]], 或模型類方法*(*內置驗證器*),具有以下特征: * * ```php * // $params 引用給驗證規則的參數 * function validatorName($attribute, $params) * ``` * * 上面的 `$attribute` 指當前正在驗證的屬性 。。。 * `$params` 包含一個數組驗證配置選項,例如:當字符串驗證時的max屬性,驗證當前的屬性值 * 可以訪問為 `$this->$attribute`. 注意 `$` before `attribute`; 這是取變量$attribute的值和使用它作為屬性的名稱訪問 * * Yii提供了一套[[Validator::builtInValidators|built-in validators]]. * 每一個都有別名,可以在指定驗證規則時使用 * * 看下面的一些例子: * * ```php * [ * // 內置 "required" 驗證器 * [['username', 'password'], 'required'], * // 內置 "string" 驗證器 用長度區間定制屬性 * ['username', 'string', 'min' => 3, 'max' => 12], * // 內置 "compare" 驗證器,只能在 "register" 場景中使用 * ['password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'], * // 一個內置驗證器 "authenticate()"方法被定義在模型類里 * ['password', 'authenticate', 'on' => 'login'], * // 一個驗證器的類 "DateRangeValidator" * ['dateRange', 'DateRangeValidator'], * ]; * ``` * * 注意,為了繼承定義在父類中的規則, 一個子類應該使用函數合並父類的規則,例如array_merge()這個函數 * * @return array validation rules返回驗證規則數組 * @see scenarios() */ public function rules() { return []; } /** * 返回一個場景列表和每個場景對應的屬性,此屬性是活動屬性 * 一個場景中的屬性只在當前場景中被驗證 * 返回的數組應該是下列格式的: * * ```php * [ * 'scenario1' => ['attribute11', 'attribute12', ...], * 'scenario2' => ['attribute21', 'attribute22', ...], * ... * ] * ``` * * 默認情況下,活動屬性被認為是安全的,並且可以被賦值 * 如果一個屬性不應該被賦值 (因此認為不安全), * 請用感嘆號前綴屬性 (例如: `'!rank'`). * * 此方法默認返回聲明中的所有屬性 [[rules()]] * 一個特殊的場景被稱為默認場景[[SCENARIO_DEFAULT]] 將會包含在rules()規則里的所有的屬性 * 每個場景將與正在應用於場景的驗證規則進行驗證的屬性關聯 * * @return array 返回一個數組和相應的屬性列表 */ public function scenarios() { //在場景數組里先把默認場景放入 $scenarios = [self::SCENARIO_DEFAULT => []]; //獲取所有場景迭代,getValidators()獲取關於驗證類對象的數組 foreach ($this->getValidators() as $validator) { //$validator->on的值是數組,獲得所有應用的場景名字 foreach ($validator->on as $scenario) { $scenarios[$scenario] = []; } //$validator->on的值是數組,獲得當前不使用的場景的名字 foreach ($validator->except as $scenario) { $scenarios[$scenario] = []; } } //$names獲得了所有的場景 $names = array_keys($scenarios); foreach ($this->getValidators() as $validator) { if (empty($validator->on) && empty($validator->except)) { //on為空數組,except也是空數組,表示驗證規則中沒有設置場景,則把驗證規則運用到所有的場景 foreach ($names as $name) { foreach ($validator->attributes as $attribute) { //把所有模型驗證屬性添加到每個場景 $scenarios[$name][$attribute] = true; } } } elseif (empty($validator->on)) { //on為空,except不為空,表示除了except場景外,應用於所有的場景 foreach ($names as $name) { if (!in_array($name, $validator->except, true)) { //找到不在except中的場景,放進場景屬性數組,表示在其它場景中驗證 foreach ($validator->attributes as $attribute) { $scenarios[$name][$attribute] = true; } } } } else { //on不為空,在on場景中驗證這些屬性 foreach ($validator->on as $name) { foreach ($validator->attributes as $attribute) { $scenarios[$name][$attribute] = true; } } } } //使每個場景名對應一個屬性數組,$scenarios的鍵是場景名 foreach ($scenarios as $scenario => $attributes) { if (!empty($attributes)) { $scenarios[$scenario] = array_keys($attributes); } } //場景名對應屬性名的數組 return $scenarios; } /** * 返回表單的名稱,就是這個 model 的類名. * * 在一個模型中表單的name值經常被使用在 [[\yii\widgets\ActiveForm]] 決定如何命名屬性的輸入字段 * 如果表單的name值是A,表單元素屬性名是b,則表單元素的name值為"A[b]" * 如果表單的name值為空字符串,則表單元素的name為"b" * * 上述命名模式的目的是針對包含多個不同模型的表單,比較容易區分不同模型的不同屬性 * 每個模型的屬性被分組在后的數據的子數組中,它是更容易區分它們。 * * 默認情況下,此方法返回模型類名 (不包含命名空間) * 你可以覆蓋此方法,當一個表單中有多個模型時 * * @return string the form name of this model class.模型類的名字 * @see load() */ public function formName() { //ReflectionClass是php中的擴展反射類 $reflector = new ReflectionClass($this); //getShortName()返回不帶命名空間的類名 return $reflector->getShortName(); } /** * 返回一個屬性列表 * 默認情況下,此方法返回類的所有公共非靜態屬性。 * 可以覆蓋此方法返回你想要的屬性 * @return array list of attribute names. */ public function attributes() { //獲取這個類的相關信息 $class = new ReflectionClass($this); $names = []; //遍歷這個類的每一個屬性,如果這個屬性是公共的,就把它放入name數組中 foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { //不是public並且不是static if (!$property->isStatic()) { $names[] = $property->getName(); } } return $names; } /** * 返回屬性的標簽 * * 屬性標簽主要用於顯示. 例如, `firstName`屬性將會顯示成`First Name`標簽,可以有友好的展示給終端用戶 * * 默認的標簽生成是使用 [[generateAttributeLabel()]]這個方法 * 此方法允許您顯式指定屬性標簽. * * 注意,為了繼承父類中定義的標簽, 子類標簽可以使用`array_merge()`與父類標簽合並 * * @return array attribute labels (name => label) * @see generateAttributeLabel() */ public function attributeLabels() { return []; } /** * 返回屬性提示 * * 屬性提示主要用於顯示. 例如,`isPublic`這個屬性可以用來描述“未登錄用戶的帖子是否應該可見” * 它提供了用戶友好的描述屬性的含義,並可以顯示給最終用戶. * * 如果省略了顯式聲明,則不會生成標記提示 * * 注意,為了繼承父類中定義的標簽, 子類標簽可以使用`array_merge()`與父類標簽合並. * * @return array attribute hints (name => hint) * @since 2.0.4 */ public function attributeHints() { return []; } /** * 執行數據驗證. * * 此方法執行適用於當前場景的驗證規則 [[scenario]]. * 下列標准用於判斷規則是否適用: * * - 規則必須與當前場景相關的屬性關聯; * - 規則在所處的情況下必須是有效的, * * validate()在執行前會先執行 [[beforeValidate()]] , * validate()執行后會執行 [[afterValidate()]] * 如果[[beforeValidate()]] 返回false,接下來的驗證將被取消 * * 驗證期間發現的錯誤可以通過 [[getErrors()]],[[getFirstErrors()]] and [[getFirstError()]]獲得錯誤信息, * * @param array $attributeNames 一個應該被驗證的屬性列表 * 若$attributeNames 為空,這意味着在適用的驗證規則中列出的任何屬性都應該經過驗證。 * @param boolean $clearErrors 表示在執行驗證前是否先清除錯誤 [[clearErrors()]] * @return boolean 驗證是否成功無任何錯誤. * @throws InvalidParamException 不知道當前場景時會拋出異常. */ public function validate($attributeNames = null, $clearErrors = true) { if ($clearErrors) { //清除所有的錯誤 $this->clearErrors(); } if (!$this->beforeValidate()) { //沒通過before驗證就返回false return false; } //返回當前活動的場景 $scenarios = $this->scenarios(); //返回此模型應用的場景 $scenario = $this->getScenario(); if (!isset($scenarios[$scenario])) { //若當前活動的場景不在此模型中,拋出異常 throw new InvalidParamException("Unknown scenario: $scenario"); } if ($attributeNames === null) { //屬性數組為空,自動查找當前場景下的安全屬性,並返回這些屬性 $attributeNames = $this->activeAttributes(); } //$this->getActiveValidators()返回Validator對象數組 foreach ($this->getActiveValidators() as $validator) { //通過Validator對象驗證屬性 $validator->validateAttributes($this, $attributeNames); } //驗證的后置方法 $this->afterValidate(); //沒有錯誤就返回真 return !$this->hasErrors(); } /** * 在驗證前被驗證 * 默認的實現提出了一個` beforevalidate `的事件 * 驗證之前,您可以重寫此方法進行初步檢查。 * 請確保調用父實現,然后就可以引發此事件。 * @return boolean是否應執行接下來的驗證,默認是真 * 如果返回false,則驗證將停止,該模型被認為是無效的 */ public function beforeValidate() { //這個不說了ModelEvent里一個方法都木有 $event = new ModelEvent; $this->trigger(self::EVENT_BEFORE_VALIDATE, $event); return $event->isValid; } /** * 驗證執行后被調用 * 廢話不多說了,可以覆蓋,記得調用父類方法 */ public function afterValidate() { $this->trigger(self::EVENT_AFTER_VALIDATE); } /** * 返回聲明在 [[rules()]]中的驗證. * * 此方法和 [[getActiveValidators()]] 不同,[[getActiveValidators()]]只返回當前場景的驗證 * * 由於該方法返回一個數組對象的對象,你可以操縱它通過插入或刪除驗證器(模型行為的有用)。 * For example, * * ```php * $model->validators[] = $newValidator; * ``` * * @return ArrayObject|\yii\validators\Validator[] 返回在模型中定義的所有驗證器 */ public function getValidators() { if ($this->_validators === null) { $this->_validators = $this->createValidators(); } return $this->_validators; } /** * Returns the validators applicable to the current [[scenario]]. * @param string $attribute the name of the attribute whose applicable validators should be returned. * If this is null, the validators for ALL attributes in the model will be returned. * @return \yii\validators\Validator[] the validators applicable to the current [[scenario]]. */ public function getActiveValidators($attribute = null) { $validators = []; $scenario = $this->getScenario(); foreach ($this->getValidators() as $validator) { if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) { $validators[] = $validator; } } return $validators; } /** * 根據 [[rules()]]里的驗證規則創建一個驗證對象. * 和 [[getValidators()]]不一樣, 每次調用此方法,一個新的列表驗證器將返回。 * @return ArrayObject validators * @throws InvalidConfigException 如果任何驗證規則配置無效,拋出異常 */ public function createValidators() { $validators = new ArrayObject; foreach ($this->rules() as $rule) { //遍歷規則中的每一項 if ($rule instanceof Validator) { ///如果規則屬於Validator對象,添加入數組對象 $validators->append($rule); } elseif (is_array($rule) && isset($rule[0], $rule[1])) { //如果子規則是數組,創建一個驗證器類,把驗證的類型,模型,屬性名,驗證屬性的初始值傳入 $validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2)); //把創建的對象加入數組對象 $validators->append($validator); } else { //拋出規則必須包含屬性名和驗證類型 throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.'); } } return $validators; } /** * 檢查屬性是否在當前場景中被應用 * This is determined by checking if the attribute is associated with a * [[\yii\validators\RequiredValidator|required]] validation rule in the * current [[scenario]]. * * 注意,當確認有條件驗證的應用,使用 * [[\yii\validators\RequiredValidator::$when|$when]] 這個方法將會返回 * `false` 不管 `when` 條件, 因為它可能在模型加載數據前被調用 * * @param string $attribute 屬性名 * @return boolean whether the attribute is required */ public function isAttributeRequired($attribute) { foreach ($this->getActiveValidators($attribute) as $validator) { if ($validator instanceof RequiredValidator && $validator->when === null) { return true; } } return false; } /** * 返回一個值,該值指示屬性是否是安全的 * @param string $attribute 屬性名 * @return boolean whether the attribute is safe for massive assignments * @see safeAttributes() */ public function isAttributeSafe($attribute) { //判斷屬性是否在數組里,true表示全等(數值和類型都相等) return in_array($attribute, $this->safeAttributes(), true); } /** * 返回一個值,該值指示當前場景中的屬性是否處於活動狀態。 * @param string $attribute 屬性名 * @return boolean whether the attribute is active in the current scenario * @see activeAttributes() */ public function isAttributeActive($attribute) { return in_array($attribute, $this->activeAttributes(), true); } /** * 返回指定屬性的文本標簽 * @param string $attribute 屬性名 * @return string 屬性標簽 * @see generateAttributeLabel() * @see attributeLabels() */ public function getAttributeLabel($attribute) { //獲得所有屬性標簽,並給$lable這個數組 $labels = $this->attributeLabels(); //如果這個方法被子類重寫了,直接返回自定義的標簽,如果沒有被重寫,返回默認的 return isset($labels[$attribute]) ? $labels[$attribute] : $this->generateAttributeLabel($attribute); } /** * 返回指定屬性的文本提示 * @param string $attribute 屬性名 * @return string the attribute hint * @see attributeHints() * @since 2.0.4 */ public function getAttributeHint($attribute) { $hints = $this->attributeHints(); //如果這個方法被子類重寫了,直接返回自定義的文本提示,如果沒有被重寫,返回'' return isset($hints[$attribute]) ? $hints[$attribute] : ''; } /** * 返回一個值,該值指示是否有任何驗證錯誤 * @param string|null $attribute attribute name. Use null to check all attributes. * @return boolean whether there is any error. */ public function hasErrors($attribute = null) { //如果有錯,_errors[$attribute]有值 return $attribute === null ? !empty($this->_errors) : isset($this->_errors[$attribute]); } /** * 返回所有屬性或單個屬性的錯誤。 * @param string $attribute attribute name. 使用NULL檢索所有屬性的錯誤 * @property array An array of errors for all attributes. 其結果是二維數組,空數組意味着沒有錯誤 * See [[getErrors()]] for detailed description. * @return array 返回一個或多個屬性所指定的錯誤。 * * ```php * [ * 'username' => [ * 'Username is required.', * 'Username must contain only word characters.', * ], * 'email' => [ * 'Email address is invalid.', * ] * ] * ``` * * @see getFirstErrors() * @see getFirstError() */ public function getErrors($attribute = null) { if ($attribute === null) { //如果屬性為空,返回所有屬性的驗證結果 return $this->_errors === null ? [] : $this->_errors; } else { //屬性不為空,驗證單個屬性返回的錯誤結果 return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : []; } } /** * 返回模型中每個屬性的第一個錯誤 * @return array the first errors. 數組的鍵是屬性名, 數組的值是錯誤信息 * 沒有錯誤就返回空數組 * @see getErrors() * @see getFirstError() */ public function getFirstErrors() { if (empty($this->_errors)) { //沒有錯誤,返回空數組 return []; } else { $errors = []; //遍歷所有屬性的第一個錯誤,放進數組 foreach ($this->_errors as $name => $es) { if (!empty($es)) { $errors[$name] = reset($es); } } return $errors; } } /** * 返回指定屬性的第一個錯誤。 * @param string $attribute 屬性名 * @return string 錯誤信息,空意味着沒有錯誤信息 * @see getErrors() * @see getFirstErrors() */ public function getFirstError($attribute) { //如果這個屬性的錯誤在驗證時存在,則返回這個錯誤,否則返回null return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null; } /** * 向指定屬性添加新錯誤 * @param string $attribute attribute name * @param string $error new error message */ public function addError($attribute, $error = '') { //把錯誤信息添加入指定屬性的數組 $this->_errors[$attribute][] = $error; } /** * 添加錯誤列表 * @param array $items 錯誤信息列表. 數組的鍵必須是屬性的名字 * 數組的值是錯誤信息. 如果一個屬性有很多錯誤,則錯誤需要是數組的形式, * 你可以使用 [[getErrors()]] 作為此參數的值 * @since 2.0.2 */ public function addErrors(array $items) { //遍歷屬性和對應的錯誤 foreach ($items as $attribute => $errors) { if (is_array($errors)) { foreach ($errors as $error) { $this->addError($attribute, $error); } } else { $this->addError($attribute, $errors); } } } /** * 刪除所有屬性或單個屬性的錯誤。 * @param string $attribute attribute name. 使用NULL刪除所有屬性的錯誤。 */ public function clearErrors($attribute = null) { if ($attribute === null) { //如果沒傳屬性,把所有的錯誤清除掉 $this->_errors = []; } else { //刪除對應屬性的錯誤 unset($this->_errors[$attribute]); } } /** * 根據給定屬性名稱生成用戶友好的屬性標簽。 * 這是通過用空格替換下划線、破折號和圓點,並將每個單詞的第一個字母替換為大寫字母。 * @param string $name the column name * @return string the attribute label */ public function generateAttributeLabel($name) { //camel2words定義了一個正則來替換字符串 return Inflector::camel2words($name, true); } /** * 返回屬性值 * @param array $names 返回需要的屬性列表 * 默認為null, 意味着定義在 [[attributes()]] 里的所有屬性都會被返回 * 如果是數組,則只返回數組中的屬性 * @param array $except 不應返回值的屬性列表 * @return array attribute values (name => value). */ public function getAttributes($names = null, $except = []) { $values = []; if ($names === null) { //$names為null,$names設置成所有的屬性 $names = $this->attributes(); } foreach ($names as $name) { $values[$name] = $this->$name; } foreach ($except as $name) { //不返回哪個屬性,從數組中刪除哪個屬性 unset($values[$name]); } return $values; } /** * 以大量的方式設置屬性值。 * @param array $values 屬性以 (name => value) 被分配到模型 * @param boolean $safeOnly 賦值是否只對安全的屬性進行 * 安全屬性是與當前中的驗證規則關聯的屬性,定義在[[scenario]]中. * @see safeAttributes() * @see attributes() */ public function setAttributes($values, $safeOnly = true) { if (is_array($values)) { //array_flip交換數組中的鍵和值,$safeOnly為true返回安全屬性,否則返回所有的屬性 $attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes()); foreach ($values as $name => $value) { if (isset($attributes[$name])) { // 如果存在該屬性,就直接賦值 $this->$name = $value; } elseif ($safeOnly) { // 如果不存在,而且是 safeOnly 的話,就觸發一下 onUnsafeAttribute 方法 $this->onUnsafeAttribute($name, $value); } } } } /** * 當一個不安全的屬性被賦值時調用此方法 * 如果是在yii_debug,默認實現會記錄一個警告消息 * @param string $name the unsafe attribute name * @param mixed $value the attribute value */ public function onUnsafeAttribute($name, $value) { if (YII_DEBUG) { //debug模式,警告信息出現 Yii::trace("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __METHOD__); } } /** * 返回在模型中應用的場景 * * 場景影響如何進行驗證,哪些屬性可以大量分配。 * * @return string the scenario that this model is in. 默認場景是 [[SCENARIO_DEFAULT]]. */ public function getScenario() { return $this->_scenario; } /** * 為模型設置場景 * 注意,此方法不檢查場景是否存在 * [[validate()]]會檢查場景是否存在. * @param string $value 再模型中的場景名. */ public function setScenario($value) { $this->_scenario = $value; } /** * 返回當前場景中大量分配的安全的屬性名稱 * @return string[] safe attribute names */ public function safeAttributes() { // 獲取當前的場景 $scenario = $this->getScenario(); // 獲取所有場景及其屬性 $scenarios = $this->scenarios(); if (!isset($scenarios[$scenario])) { // 場景不存在,就返回空 return []; } $attributes = []; foreach ($scenarios[$scenario] as $attribute) { // 將開頭不是!的屬性才會放入到 $attributes 中 if ($attribute[0] !== '!' && !in_array('!' . $attribute, $scenarios[$scenario])) { $attributes[] = $attribute; } } return $attributes; } /** * 返回當前場景中要受驗證的屬性名稱 * @return string[] 返回安全的屬性名稱 */ public function activeAttributes() { $scenario = $this->getScenario(); $scenarios = $this->scenarios(); if (!isset($scenarios[$scenario])) { return []; } // 獲取當前場景中的所有屬性 $attributes = $scenarios[$scenario]; foreach ($attributes as $i => $attribute) { if ($attribute[0] === '!') { // 如果屬性名以!開頭,就把!截取掉,並放入數組 $attributes[$i] = substr($attribute, 1); } } return $attributes; } /** * 填充模型的輸入數據(把數據加載到模型中) * * 這種方法提供了一個方便快捷的方式: * * ```php * if (isset($_POST['FormName'])) { * $model->attributes = $_POST['FormName']; * if ($model->save()) { * // handle success * } * } * ``` * * 如果使用load方法 * * ```php * if ($model->load($_POST) && $model->save()) { * // handle success * } * ``` * * `load()` gets the `'FormName'` from the model's [[formName()]] method (which you may override), unless the * `$formName` parameter is given. If the form name is empty, `load()` populates the model with the whole of `$data`, * instead of `$data['FormName']`. * * 注意,被填充的數據將會接受[[setAttributes()]]的安全檢查. * * @param array $data 模型加載的數組, 典型的是 `$_POST` or `$_GET`. * @param string $formName 用於將數據加載到模型中的表單名稱 * If not set, [[formName()]] is used. * @return boolean whether `load()` found the expected form in `$data`. */ public function load($data, $formName = null) { //如果沒有傳表單名稱,就取所在類的名稱 $scope = $formName === null ? $this->formName() : $formName; if ($scope === '' && !empty($data)) { //如果 $scope 為空字符串,且 $data不為空,就設置屬性 $this->setAttributes($data); return true; } elseif (isset($data[$scope])) { // 存在 $data[$scope],使用 $data[$scope] 去設置屬性 $this->setAttributes($data[$scope]); return true; } else { return false; } } /** * 從終端用戶獲取數據,形成模型 * 該方法主要用於收集表格數據輸入 * 為每個模型加載的數據`$data[formName][index]`, where `formName` * refers to the value of [[formName()]], and `index` the index of the model in the `$models` array. * If [[formName()]] is empty, `$data[index]` 將用於填充每個模型. * 每個模型的數據都要經過安全檢查 [[setAttributes()]]. * @param array $models 要填充的模型 注意所有的模型都應該有相同的類 * @param array $data the data array. 通常是 `$_POST` or `$_GET`, 也可以是最終用戶提供的任何有效數組。 * @param string $formName 要把數據加載到模型的表單名 * If not set, it will use the [[formName()]] value of the first model in `$models`. * This parameter is available since version 2.0.1. * @return boolean whether at least one of the models is successfully populated. */ public static function loadMultiple($models, $data, $formName = null) { if ($formName === null) { /* @var $first Model */ //reset — 將數組的內部指針指向第一個單元 $first = reset($models); if ($first === false) { // 不存在就返回 false return false; } // 拿到所在類的名稱 $formName = $first->formName(); } $success = false; // 遍歷 $models,一個個 load 數據 foreach ($models as $i => $model) { /* @var $model Model */ if ($formName == '') { if (!empty($data[$i])) { // 數據不為空,就 load 到相應的 model 中 $model->load($data[$i], ''); $success = true; } } elseif (!empty($data[$formName][$i])) { // 存在 $formName,且數據不為空,就 load 到相應的 model 中 $model->load($data[$formName][$i], ''); $success = true; } } return $success; } /** * 驗證多模型 * 這種方法將驗證每一個模型。被驗證的模型可以是相同的或不同類型的。 * @param array $models 要驗證的模型 * @param array $attributeNames 應該驗證的屬性名稱列表。 * 如果這個參數是空的,它意味着在適用的驗證規則中列出的任何屬性都應該被驗證。 * @return boolean 所有模型的驗證規則是否有效. 一個或多個驗證不通過都會返回false */ public static function validateMultiple($models, $attributeNames = null) { $valid = true; /* @var $model Model */ foreach ($models as $model) { //遍歷$models 調用validate()方法 $valid = $model->validate($attributeNames) && $valid; } return $valid; } /** * 當沒有指定特定字段時,通過[[toArray()]] 方法返回默認情況下應返回的字段列表 * * 此方法應返回字段名稱或字段定義的數組 * 當沒有指定特定字段時,字段名稱將被視為對象屬性名稱,其值將用作字段值 * 當指定字段時,數組鍵應該是字段名,而數組值應該是相應的字段定義,它可以是對象屬性名,也可以是PHP可調用返回相應字段值 * *回調函數格式為: * ```php * function ($model, $field) { * // return field value * } * ``` * * 例如,下面的代碼聲明四個字段: * * - `email`: 字段名稱與屬性名稱相同`email`; * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their * values are obtained from the `first_name` and `last_name` properties; * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name` * and `last_name`. * * ```php * return [ * 'email', * 'firstName' => 'first_name', * 'lastName' => 'last_name', * 'fullName' => function ($model) { * return $model->first_name . ' ' . $model->last_name; * }, * ]; * ``` * * 在此方法中,還可以根據一些上下文信息返回字段的不同列表 * 例如,取決於場景[scenario]或當前應用程序用戶的特權 * 您可以返回不同的可見字段集或篩選一些字段。 * * 此方法返回[[attributes()]] 的默認實現,索引是屬性名 * * @return array 返回字段名稱或字段定義的列表. * @see toArray() */ public function fields() { $fields = $this->attributes(); //合並兩個數組來創建一個新數組,其中的一個數組元素為鍵名,另一個數組元素為鍵值 return array_combine($fields, $fields); } /** * 返回用於遍歷模型中的屬性的迭代器 * 此方法所需的接口[[\IteratorAggregate]]. * @return ArrayIterator 遍歷列表中的項的迭代器 */ public function getIterator() { $attributes = $this->getAttributes(); return new ArrayIterator($attributes); } /** * 返回指定偏移量是否有元素 * 此方法需要SPL接口 [[\ArrayAccess]]. * 它會隱式調用 `isset($model[$offset])`. * @param mixed $offset 檢查的偏移量 * @return boolean 是否存在偏移 */ public function offsetExists($offset) { return isset($this->$offset); } /** * 返回指定偏移量的元素 * 此方法需要SPL接口 [[\ArrayAccess]]. * 會隱式調用 `$value = $model[$offset];`. * @param mixed $offset the offset to retrieve element. * @return mixed the element at the offset,如果在偏移處沒有找到元素,返回null */ public function offsetGet($offset) { return $this->$offset; } /** * 設置指定偏移量的元素 * 此方法需要SPL接口[[\ArrayAccess]]. * 會被隱式調用 `$model[$offset] = $item;`. * @param integer $offset the offset to set element * @param mixed $item 節點的值 */ public function offsetSet($offset, $item) { $this->$offset = $item; } /** * 將指定偏移量的元素值設置為空 * 此方法需要SPL接口 [[\ArrayAccess]]. * 會隱式調用 `unset($model[$offset])`. * @param mixed $offset the offset to unset element */ public function offsetUnset($offset) { $this->$offset = null; } }
