Yii load方法小探&&感覺思想

前面的話

Yii有一系列語法糖可供使用,其中就包含Model的load方法。
前一家公司用的是Laravel,剛接觸Yii load方法的時候以爲蠻二的,就這麼輕鬆的把提交上來的數據加載進Model了,不過用習慣了確實省事。感受上這麼作太偷懶了,沒有Laravel封的那麼緊。但尊重框架的東西吧。html

開始正文

平時作頁面的時候前端用Yii的Form生成的表單,controller直接load進Model也就存起來了,此次是跟外部有一個同步對接的活,把外部的一些數據導入過來,因而也就想固然用load()了,結果對面報失敗,查日誌調試跟蹤,啊咧,居然返回false?!前端

習慣性的抓一下$model->getErrors(),誒?居然沒用數據。
看代碼,調試跟進。數據庫

對方傳過來的數據結構是這樣的:數組

[
    [sign] => 'string_sign'
    [timestamp] => 1488338067
    [data] => [
                'key1' => 'value1',
                'key2' => 'value2',
                'key3' => 'value3',
                'key4' => 'value4',
              ]
]

一、第一階段,覺得本身這麼作就對了,呵呵噠,你太naive了

首先分析load方法:數據結構

public function load($data, $formName = null)
{
    $scope = $formName === null ? $this->formName() : $formName;
    if ($scope === '' && !empty($data)) {
        $this->setAttributes($data);

        return true;
    } elseif (isset($data[$scope])) {
        $this->setAttributes($data[$scope]);

        return true;
    } else {
        return false;
    }
}

當時load的時候,第一個參數$data = Yii::$app->request->post('data');app

因爲$formName === null,因此$scope = $this->formName(),那麼$scope是多少。框架

public function formName()
{
    $reflector = new ReflectionClass($this);
    return $reflector->getShortName();
}

formName()方法調用反射$reflector->getShortName()獲取當前類的短名,不含命名空間的類名。yii

好,回到代碼,$scope就是實際Model的類名,假設$scope = 'Merchant'post

public function load($data, $formName = null)
{
    $scope = $formName === null ? $this->formName() : $formName;
    // $scope == 'Merchant'
    if ($scope === '' && !empty($data)) {
        $this->setAttributes($data);

        return true;
    } elseif (isset($data[$scope])) {
        $this->setAttributes($data[$scope]);

        return true;
    } else {
        return false;
    }
}

因爲$scope不爲空,因此邏輯就走到了elseif中
elseif中的判斷,elseif (isset($data[$scope])),因爲$data直接就是數據庫的字段和值的對應,因此也不知足,因而就走到了最後的else,返回了falsethis

好,那就改吧,把原先的數據從$post['data']變成$post['data']['Merchant']

二、第二階段,繼續深究

發現改了以後仍是false,那麼進$this->setAttributes($data[$scope]);看看

public function setAttributes($values, $safeOnly = true)
{
    if (is_array($values)) {
        $attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes());
        foreach ($values as $name => $value) {
            if (isset($attributes[$name])) {
                $this->$name = $value;
            } elseif ($safeOnly) {
                $this->onUnsafeAttribute($name, $value);
            }
        }
    }
}

因爲load的時候只傳了第一個參數,因此第二個參數$safeOnly == true,因此代碼中
$attributes = array_flip($safeOnly ? $this->safeAttributes() : $this->attributes());
的實際操做是
$attributes = array_flip($this->safeAttributes());
重點來了,這裏實際調用的是safeAttributes,好,到這就明白了,對方傳過來的某些參數沒在model中rule配置的屬於safeAttributes的地方。

解決方案:把傳來的參數中沒在safe的字段加入safe配置。
好了,完美。

過後一根菸

因此問題就很明顯了,一共有兩個問題點:
一、使用load的時候,鍵值對要被包含在數組key爲類名的裏面;
二、想用load,必需要保證字段都被配置在了Model的rule裏面的屬於safe的字段。

實際上當時看到if (isset($data[$scope]))的時候就想到了,使用Yii本身建立的Form提交數據的時候,value的name並不直接是字段名,而是lei_ming[ziduan_ming]

另外,其實Yii設置load只是爲了讓Yii Form用,所以那些字段都是「safe」的,並非爲了讓你偷懶而用load的。

for-SEO:http://blog.lovemydeer.com/20...

相關文章
相關標籤/搜索