場景還原
UserModel
class UserModel extends Model {
public function role()
{
return $this->belognsTo(RoleModel::class , 'role_id' , 'id');
}
}
出錯的程序
$user = UserModel::with('role')->find(1);
// $user->role 是一個 RoleModel
// 更新 role 屬性
$user->role = 'test';
// 正確輸出 test
var_dump($user->role);
// 但是!!轉換成 json 字符串後
// 你會發現,role 居然還是個模型!!
// 並不是你後面設置成的 test !
// 怪胎,丟失更新了?Laravel Bug ??
// 實際上不是!請看下屬描述
var_dump(json_encode($user));
原理概述
Laravel
的 Illuminate\Database\Eloquent\Model
實現了 JsonSerializable
接口,所以在調用 json_encode
進行序列化時,會調用 Model::jsonSerialize
方法,他這個方法返回的數據是:
array_merge($attribute , $relation);
實際上你通過:
$model->name = 'grayVTouch';
這種方式附加的新屬性,Laravel
通過 __set
魔術方法重載,將其添加到 attribute
數組中,你是無法更改 relation
數組的!
而通過 模型關聯 你卻可以爲 relation
數組新增單元!
看到上面的數組合並方式,可以知道 relation
會覆蓋掉 attribute
中的同名屬性!!因而要特別注意:如果 relation
中有和 attribute
中同名的屬性,請修改 relation
關聯名稱!如果不想修改 relation
名稱,堅持前者覆蓋後者,請:
// 保存值
$attr = $model->attr;
// 刪除屬性:attribute / relation 中的屬性(Laravel 內部調用 __unset 魔術方法)
unset($model->attr)
// 重新設置值,僅設置到 attribute 數組
// relation 並不會被設置
$model->attr = $model;
綜合評價
Laravel
由於將模型屬性拆分成兩個數組,而他們實際上又同屬於一個對象!所以如果存在同名屬性,必然會產生 誰覆蓋誰 的問題,attribute
一開始就是對應數據庫表中的字段的,而 relation
是後面程序附加的,爲了不丟失更新,後者覆蓋前者,非常正確。
雖然在使用過程中應該小心避免 relation
和 attribute
撞上同名屬性,但偶爾還是會碰到的~,這個還是稍微注意下就好,這並非 Bug
,而是在當前的程序處理方式下必然會產生的一個正常現象。