修改器是模型的三大利「器」之一,本篇我們來總結下修改器的用法,以及一些注意事項。
定義修改器
修改器的作用是在模型物件資料寫入資料庫之前進行一些必要的資料處理,修改器的標準定義如下:
public function setFieldNameAttr($value, $data) { // 对value值进行处理 data参数是当前全部数据 // 返回值就是实际要写入数据库的值 return $value; }
其中FieldName對應資料表的field_name欄位(注意資料表欄位的規範和修改器方法定義規範,否則會導致錯誤)。
原則上,每個修改器應僅處理對應欄位的數據,但在必要的情況下允許同時處理多個欄位。
下面是一個例子
public function setBirthdayAttr($value, $data) { // 格式化生日数据 $birthday = strtotime($value); // 根据生日判断年龄 $age = getAgeByBirthday($birthday); // 赋值年龄数据 $this->setAttr('age', $age); return $birthday; } public function setAgeAttr($value,$data) { return floor($value); }
之所以使用setAttr方法是確保年齡賦值操作仍然可以走單獨的修改器。如果你沒有額外的修改器,那麼也可以寫成
public function setBirthdayAttr($value, $data) { // 格式化生日数据 $birthday = strtotime($value); // 根据生日判断年龄 $age = getAgeByBirthday($birthday); // 赋值年龄数据 $this->data['age'] = $age; return $birthday; }
注意一定不能寫成
$this->age = $age;
因為在模型內部進行資料物件的賦值,會因為和模型內部屬性混淆而導致不可預知的後果。
如果你在某個修改器中可能會對其它欄位進行修改,務必記得你需要額外修改的欄位修改器必須已經經過賦值操作(或已經觸發過修改器)。
如何呼叫
修改器方法不需要手動調用,依照定義規範定義好後,系統會在下面的情況下自動調用:
# ·模型物件賦值;
·呼叫模型的data方法,且第二個參數傳入true;
·#調用模型的save方法,並且傳入陣列資料;
‧明確呼叫模型的setAttr方法;
·定義了該欄位的自動完成;
例如User模型定義了setPasswordAttr修改器方法。
public function setPasswordAttr($value, $data) { return md5($value); }
當下面這樣使用的時候,儲存到資料庫的password欄位的值就會變成md5('think')後的值。
$user = User::get(1); $user->password = 'think'; $user->save();
如果你在某些情況下,不希望使用修改器而是想要手動控制數據,可以嘗試使用下面的方法。
$user = User::get(1); $user->data('password', md5('think')); $user->save();
這個時候就不會經過修改器處理。
避免衝突
很多開發者喜歡為修改器定義自動完成auto(包括insert和update)。
protected $auto = ['password'];
這在V5.1.27版本之前是一個看似聰明卻非常致命的錯誤,要盡量避免,因為根據我們之前給出的修改器觸發條件,會導致該修改器被執行兩次。這會是一個災難性的錯誤,將導致所有的用戶註冊後都無法正常登入。
解決辦法取消password欄位的自動完成設置,因為修改器會在每次賦值的時候自動觸發,如果沒有賦值說明密碼沒有被修改,也談不上自動完成。
自動完成的字段通常是不在表單裡面的字段,一般是由系統自動處理的字段。
V5.1.27版本改進了這個問題,所有的修改器只允許執行一次,上面的問題就不存在了。但好像又帶來了一個新的問題,很多時候,你或許會想在模型的事件中修改資料。
User::beforeUpdate(function($user) { $user->password = md5('think'); });
會發現,在模型beforeUpdate事件中,資料的值怎麼都修改不了,原因是模型的修改器之前在第一次賦值的時候已經執行了,第二次再賦值的時候已經無效了(不會再執行)。
解決方法就是我前面提過的使用data方法不呼叫修改器進行資料賦值操作。
User::beforeUpdate(function($user) { $user->data('password', md5('think')); });
當然,更好的建議是規劃好修改器、自動完成和模型事件的數據處理機制,不要對一個字段同時使用多重機制修改數據,並且寫入數據庫的數據應該並且只有修改器這一途徑進行資料修改操作。
類型自動轉換
如果你的修改器只是對資料做型別轉換處理的話,可以不需要定義修改器,而是直接定義欄位類型就可以了。
public function setScoreAttr($value, $data) { return (float) $score; }
上面的修改器方法可以直接改成
protected $type = [ 'score' => 'float', ];
如果你同時對一個欄位定義了修改器和型別的話,修改器是優先的。
類型定義不僅能定義簡單的資料類型,還有一些額外的用途,例如:json 類型、array類型和object 類型會進行JSON序列化,serialize類型則會把資料進行serialize序列化。
PHP中文網,有大量免費的ThinkPHP入門教學,歡迎大家學習!
本文轉自:https://blog.thinkphp.cn/817548
以上是ThinkPHP:模型三大利器之二(修改器)的詳細內容。更多資訊請關注PHP中文網其他相關文章!