修改器是模型的三大利「器」之一,本篇我們來總結下修改器的用法,以及一些注意事項。
定義修改器
修改器的作用是在模型對象數據寫入數據庫之前進行一些必要的數據處理,修改器的標準定義如下:
public?function?setFieldNameAttr($value,?$data) { ????//?對value值進行處理?data參數是當前全部數據 ????//?返回值就是實際要寫入數據庫的值 ????return?$value; }
其中FieldName對應數據表的field_name字段(注意數據表字段的規范和修改器方法定義規范,否則會導致錯誤)。
立即學習“PHP免費學習筆記(深入)”;
原則上,每個修改器應當僅處理對應字段的數據,但在必要的情況下允許同時處理多個字段。
下面是一個例子
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:模型三大利器之二(