该用 fill() 时用 fill(),必须用 forcefill() 时才用 forcefill();fill() 受 $fillable/$guarded 保护,只填充白名单字段,而 forcefill() 绕过保护、不触发事件/访问器/验证,仅用于后台脚本、测试等可信场景。

什么时候该用 fill(),什么时候必须用 forceFill()
关键看字段是否在模型的 $fillable 或 $guarded 里。Laravel 的批量赋值保护机制默认只允许 $fillable 列表里的字段被 fill()、create()、update() 等方法写入;不在列表里的字段会被直接忽略——哪怕你传了值,也进不了模型实例。
forceFill() 是绕过这个检查的唯一合法方式,它不看 $fillable 或 $guarded,把所有键值对原样塞进模型属性。但这也意味着你得自己承担风险:比如意外覆盖 updated_at、id,或者写入本该由数据库自增/触发器生成的字段。
- 日常创建/更新用户提交的数据 → 用
fill()或create(),依赖$fillable做白名单控制 - 后台脚本补全缺失字段(如迁移后填充
slug)、测试中绕过校验、或给软删除字段deleted_at赋 null → 可用forceFill() - 千万别在控制器里对用户输入调
forceFill(),等于主动关掉 Laravel 最基础的安全阀
fill() 被静默忽略的常见原因
不是代码写错了,而是模型配置和数据没对上。最典型的是字段名拼错、大小写不一致,或者忘了加引号导致 PHP 把常量当变量解析(比如写成 status = ACTIVE 却没定义 ACTIVE 常量)。
PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。
-
$fillable里写的是'user_id',但请求传的是userId→ 不匹配,被丢弃 - 模型用了
$guarded = ['*']却没设$fillable→ 所有字段默认被保护,fill()什么也填不进去 - 字段在数据库是
is_active,但$fillable写成'isActive'→ Laravel 不做蛇形转驼峰自动映射,严格按字符串匹配 - 用
new User($data)构造时,等价于调fill(),同样受保护机制约束
forceFill() 的副作用和兼容性注意点
它不触发模型事件(saving、updating),也不走访问器(accessor)和修改器(mutator),更不会验证规则。这意味着你设的值就是最终入库的值,中间没有任何拦截或转换。
- 如果字段有
setCreatedAtAttribute()修改器,forceFill(['created_at' => '2020-01-01'])会跳过它,直接写入原始字符串 - Laravel 9+ 对
forceFill()加了类型检查:传入非数组会抛InvalidArgumentException,而旧版本只是静默失败 - 关联关系字段(如
posts())不能通过forceFill()设置,它只处理模型自身的属性,不处理关系 - 批量操作如
User::upsert()或insert()不走fill()/forceFill()流程,它们是直连查询构造器的
测试时怎么安全地绕过批量赋值检查
测试中经常需要快速构造带敏感字段(如 email_verified_at、remember_token)的模型,又不想开 forceFill()。更稳妥的方式是临时扩展 $fillable,而不是硬编码绕过。
- 在测试方法里加
User::$fillable[] = 'email_verified_at';,用完再array_pop()恢复 - 用工厂(Factory)时,在
definition()里直接赋值:'email_verified_at' => now(),工厂不走批量赋值流程 - 如果必须用
forceFill(),确保只在setUp()或具体测试方法内使用,避免污染其他测试用例的状态 - 别在生产代码里留
forceFill()的 TODO 注释,这种“先跑通再说”的写法很容易被遗忘,变成线上漏洞
真正难的不是选哪个方法,而是每次写 fill() 前,得想清楚这个字段到底该不该由外部控制——这比记住 API 差异重要得多。










