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', ] ]
首先分析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...