Eloquent 修改器用于属性赋值时自动转换,不触发数据库操作;需定义为 public set{Attribute}Attribute 方法并手动赋值到 $this->attributes,对 create/update/save 有效但对 DB::table()->update() 无效。

直接改数据库字段值,Eloquent 修改器(mutator)不是用来“替代 SQL UPDATE”的,而是让你在模型属性被赋值时自动做转换——比如存入前转小写、加密、格式化时间;访问时再反向处理。它不触发数据库操作,也不影响查询条件。
如何定义一个 Eloquent 修改器
修改器方法名必须是 set{Attribute}Attribute 格式,其中 {Attribute} 是驼峰命名的字段名(如数据库字段 user_name → UserName)。
- 方法必须是
public,且接受一个参数(即传入的原始值) - 方法内需调用
$this->attributes['xxx'] = ...手动赋值到底层属性 - 不要在方法里 return 任何东西(Laravel 不读取返回值)
class User extends Model
{
public function setUserNameAttribute($value)
{
$this->attributes['user_name'] = strtolower($value);
}
}
这样写完后:$user->user_name = 'John DOE'; 实际存入数据库的是 'john doe'。
修改器和访问器(accessor)别混用
修改器只在「设置属性」时触发($model->xxx = ... 或 $model->fill([...])),而访问器(get{Attribute}Attribute)只在「读取属性」时触发($model->xxx)。两者独立运行,互不影响。
- 如果你希望读取时也转小写,得额外定义访问器:
getUserNameAttribute - 如果只定义了修改器,但没定义访问器,读出来的还是数据库原值(比如存的是
'john doe',读出来就是'john doe',不会自动首字母大写) - 修改器对
create()、update()、save()都生效,但对DB::table()->update()完全无效
日期字段慎用修改器替代 $dates 或 casts
别用修改器手动处理时间字段(比如把字符串转 Carbon),这容易出错且重复造轮子:
-
$dates属性已废弃,应改用$casts:protected $casts = ['created_at' => 'datetime']; - 若要自定义时间格式(如存为 YmdHis 字符串),用
cast更安全:'published_at' => 'custom_datetime:YmdHis' - 硬写修改器处理时间,可能绕过 Laravel 的时区转换逻辑,导致存储值与
Carbon实例行为不一致
批量赋值(fill() / create())时修改器是否生效
生效,但仅限于「能匹配到模型属性名」的键。注意字段映射关系:
-
User::create(['user_name' => 'ABC'])→ 触发setUserNameAttribute -
User::create(['name' => 'ABC'])→ 不触发(除非你有name字段并定义了对应修改器) - 如果数据库字段是
first_name,但你想通过$user->firstName设置,则修改器必须叫setFirstNameAttribute,且确保first_name在$fillable中 - 使用
forceFill()会跳过所有修改器和访问器
真正容易被忽略的是:修改器只作用于模型实例上的属性操作,它不改变数据库 schema,也不参与查询构建。想在 where() 里用转换后的值?不行——得用 whereRaw() 或数据库函数,或者换用查询作用域(scope)。









