学习yii2.0框架阅读代码(八)

vendor/yiisoft/yii2/base/Model.php(续

    /**
     * 设置属性值
     * @param array $values attribute values (name => value) to be assigned to the model.
     * @param boolean $safeOnly whether the assignments should only be done to the safe attributes.
     * 一个安全属性是与一个验证规则在当前的[[情况]]。
     * @see safeAttributes()
     * @see attributes()
     */
    public function setAttributes($values, $safeOnly = true)
    {
        // 必须是个数组
        if (is_array($values)) {
            // array_flip — 交换数组中的键和值
            // 将属性放到了 key 上
            // 默认取 safeAttributes 中的属性
            $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);
                }
            }
        }
    }

    /**
     * This method is invoked when an unsafe attribute is being massively assigned.
     * The default implementation will log a warning message if YII_DEBUG is on.
     * It does nothing otherwise.
     * @param string $name the unsafe attribute name
     * @param mixed $value the attribute value
     */
    public function onUnsafeAttribute($name, $value)
    {
        if (YII_DEBUG) {
            // 如果是调试状态,就打 log 记录下,没有成功设置的不安全的属性
            Yii::trace("Failed to set unsafe attribute '$name' in '" . get_class($this) . "'.", __METHOD__);
        }
    }

    /**
     * Returns the scenario that this model is used in.
     *
     * Scenario affects how validation is performed and which attributes can
     * be massively assigned.
     *
     * @return string the scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]].
     */
    public function getScenario()
    {
        // 获取当前的场景
        return $this->_scenario;
    }

    /**
     * Sets the scenario for the model.
     * Note that this method does not check if the scenario exists or not.
     * The method [[validate()]] will perform this check.
     * @param string $value the scenario that this model is in.
     */
    public function setScenario($value)
    {
        // 设置当前的场景
        $this->_scenario = $value;
    }

    /**
     * Returns the attribute names that are safe to be massively assigned in the current scenario.
     * @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) {
            // 将开头不是!的属性才会放入到 safeAttributes 中, 即以!开头的属性不会被放到 safeAttributes 中
            if ($attribute[0] !== '!') {
                $attributes[] = $attribute;
            }
        }

        return $attributes;
    }

    /**
     * Returns the attribute names that are subject to validation in the current scenario.
     * @return string[] safe attribute names
     */
    public function activeAttributes()
    {
        // 同上
        $scenario = $this->getScenario();
        $scenarios = $this->scenarios();
        if (!isset($scenarios[$scenario])) {
            return [];
        }
        // 获取当前场景中的所有属性
        $attributes = $scenarios[$scenario];
        foreach ($attributes as $i => $attribute) {
            // 如果属性名以!开头,就把!截取掉
            // !开头的属性来自rules,加!能够使规则(即 validator)生效,但却能够不出现在 safeAttributes 中
            if ($attribute[0] === '!') {
                $attributes[$i] = substr($attribute, 1);
            }
        }

        return $attributes;
    }

    /**
     * Populates the model with the data from end user.
     * The data to be loaded is `$data[formName]`, where `formName` refers to the value of [[formName()]].
     * If [[formName()]] is empty, the whole `$data` array will be used to populate the model.
     * The data being populated is subject to the safety check by [[setAttributes()]].
     * 加载数据到所在的 model 中
     * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array
     * supplied by end user.
     * @param string $formName the form name to be used for loading the data into the model.
     * If not set, [[formName()]] will be used.
     * @return boolean whether the model is successfully populated with some data.
     */
    public function load($data, $formName = null)
    {
        // 如果存在 yii 的 form,就使用该 form,否则就拿到所在类的名称(不含 namespace)
        $scope = $formName === null ? $this->formName() : $formName;
        if ($scope === '' && !empty($data)) {
            // 如果 $scope 为空字符串,且 $data不为空,就设置属性
            // 即 $formName 为空字符串,且 $data不为空
            $this->setAttributes($data);

            return true;
        } elseif (isset($data[$scope])) {
            // 否则,必须存在 $data[$scope],使用 $data[$scope] 去设置属性
            $this->setAttributes($data[$scope]);

            return true;
        } else {
            return false;
        }
    }

    /**
     * Populates a set of models with the data from end user.
     * 加载数据到所在的 model 的集合中
     * This method is mainly used to collect tabular data input.
     * The data to be loaded for each model is `$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]` will be used to populate each model.
     * The data being populated to each model is subject to the safety check by [[setAttributes()]].
     * @param array $models the models to be populated. Note that all models should have the same class.
     * @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array
     * supplied by end user.
     * @param string $formName the form name to be used for loading the data into the models.
     * 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;
            }
            // 拿到所在类的名称(不含 namespace)
            $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;
    }

原文地址:https://www.cnblogs.com/xwzj/p/5403021.html