必须显式声明$fillable,禁用$guarded;$fillable是白名单机制,未列字段静默丢弃,是模型层最后一道防线,而$guarded易漏导致安全漏洞。

直接说结论:用 fill() 或 create() 前,必须显式声明 $fillable;别碰 $guarded,它漏字段就是漏洞。
为什么 $fillable 是唯一靠谱的批量赋值入口
因为 Laravel 默认禁止所有字段批量写入,$fillable 是白名单——没列进去的字段,哪怕请求里带着、验证也通过了,fill()、create()、update() 全部静默丢弃,不报错也不入库。这是模型层最后一道防线。
-
$guarded是黑名单,新增字段时极易漏写,一漏就裸奔(比如加了个is_verified字段但没加进$guarded,前端 POST 就能改) - Laravel 官方从 5.8 起已明确建议弃用
$guarded,文档里都不再主推 -
$fillable = ['*']看似省事,实则等于关掉保护,等同于把数据库钥匙扔给任意请求 - 真实字段名才可进
$fillable:比如迁移里是email_verified_at,就不能写email_verified;id、created_at、updated_at除非真有业务要手动控制,否则别放进去
fill()、create()、update() 的行为差异和坑点
三者都走同一套批量赋值逻辑,但触发时机和隐含风险不同:
-
create()是静态方法,内部先 new 实例再fill()再save();它只受$fillable约束,不触发模型事件(如creating)或访问器(setPasswordAttribute)——除非你重写了create() -
fill()是实例方法,必须配合save()手动调用;它会触发访问器/修改器,适合需要数据预处理的场景 -
update()是静态方法,底层调用fill()+save(),但它会把主键(如id)也当普通字段处理——如果请求里带了id且你在$fillable里漏掉了它,update()可能意外覆盖主键(尤其配合upsert()时) -
$request->all()永远别直接喂给这些方法;哪怕模型设了$fillable,也要先过滤:$user->fill($request->only(['name', 'email']))或用$request->validated()
forceFill() 什么时候能用、什么时候绝对不能用
forceFill() 绕过 $fillable、不触发访问器、不调事件、不走验证——它是给可信后台脚本留的后门,不是给 API 控制器用的。
- 能用的场景:
php artisan db:seed、console 命令填充测试数据、migration 中补字段值 - 绝不能用的场景:任何来自 HTTP 请求的数据,哪怕你刚
validate()过;哪怕你只填一个字段,也该用update(['field' => $value])而不是forceFill() -
forceFill()会原样塞进属性,包括updated_at、id、password_hash—— 如果你没意识到这点,可能覆盖掉数据库自增 ID 或时间戳 - 临时放开某字段?别改模型的
$fillable数组(线程不安全),更别用forceFill();正确做法是显式传参:$user->update(['is_active' => true])
最常被忽略的一点:批量赋值保护和验证器是两层事。验证器确保“数据合法”,$fillable 确保“字段可控”。哪怕你写了 'is_admin' => 'boolean' 验证规则,只要没把它放进 $fillable,它就进不了模型;但如果你忘了加,它就直接进了——而且不会报错,只会安静地改掉权限。











