最近在使用場景的時候碰到一些不解的問題,我簡單模擬下: 我新建一張表info,有name和age2個字段 模型代碼:ios
public function rules() { return [ [['age'], 'integer'], [['name'], 'string', 'max' => 22], ['name','required','message'=>'姓名不能爲空','on'=>['add','update']], ['age','required','message'=>'年齡不能爲空','on'=>'add'], ['age','isMath','on'=>'add'] ]; } public function scenarios() { return [ 'add' => ['age','name'], 'update'=>['name'] ]; }
當我進行編輯操做的時候,使用$model->scenario = 'update';也就是使用update場景,出現的結果是:name改變了age居然沒有更改,我把代碼修改下數據庫
public function scenarios() { return [ 'add' => ['age','name'], 'update'=>['name','age'] //這裏加個age ]; }
這個時候正常修改了name和age字段數組
還有就是我不重寫scenarios()方法,也是正常執行驗證的框架
因此有2個問題: 1.重寫scenarios()方法,具體應該在何時? 2.上面代碼加個age和不加age區別是什麼?在update場景下我只驗證name字段,age只在add場景下,可是在update場景下'update'=>['name']沒有寫age就不能修改age字段?????yii
答:ui
默認狀況下,scenarios() 返回的是一個關係數組,鍵是模型中全部可用的場景,值是場景對應的 active attributes 列表。active attributes 有兩個特色:code
$model->load() && $model->save()
保存數據時只保存 active attributes;在你的例子中,把 'age' 從 update 場景中移除意味着 age 再也不是 active attribute, 這就是爲何對 age 的修改沒有寫入數據庫的緣由。get
默認的 scenarios() 過程(以你的例子中聲明的規則爲例)string
scenarios() 會進行兩次遍歷操做。首先遍歷 rules 中聲明的規則,把全部可用的場景找出來,並初始化數組,以你的例子中的 rules 爲例,值爲:io
[ 'default' => [], // 框架自帶的默認場景 'add' => [], 'update' => [], ]
第二次遍歷時,對每一個 rule 中聲明的 attributes 「對號入座」,若是 rule 中不帶 'on' 屬性,表示該屬性所屬的規則適用於全部場景(例如 [['age'], 'integer']
), 會將 'age' 追加到全部場景的 active attibutes 列表中;若是指定了場景值,僅在對應的場景下的active attributes 內追加。
通過這兩次遍歷,scenarios() 最終返回的值將是:
[ 'default' => ['age', 'name'], 'add' => ['age', 'name'], 'update' => ['age', 'name'], ]
能夠看到, 'age' 在每一個場景中都是 active attribute, 這就是爲何在你不重寫 scenarios() 的狀況下,對 age 的修改也能保存的緣由。
瞭解了這個過程,個人理解是:在大多數狀況下,是不須要重寫 scenarios() 的,經過配置 rules() 來改變 scenarios() 的返回值,而不是直接手動覆蓋 sceanrios(). 覆蓋帶來的一個明顯的弊端是:你的精力被分散到了兩個方法內(rules() 和 scenarios()),假設未來你的 info 表又新增了一列,在 rules() 追加新的規則的同時,你還要修改重寫後的 scenarios(), 把新增長的列追加進去使其成爲 active attribute,不然就會出現新增列的數據沒法存入數據庫。使用默認的 scenarios() 的話,只須要將精力放在 rules() 上便可。