thinkphp 6+ 中 $fillable 不生效是因为默认启用严格字段检查,未在白名单中的字段会直接报错而非忽略;需补全 $fillable 或用 allowfield() 动态放行,且字段过滤优先于验证执行。

为什么 protected $fillable 在 ThinkPHP 6+ 里突然不生效了
因为 ThinkPHP 6 开始默认启用「严格字段检查」,模型不再自动忽略未声明字段,而是直接抛出 InvalidArgumentException:「The field xxx is not exists in table xxx」。这不是 bug,是安全加固后的主动拦截。
常见现象:用 create() 或 save() 批量赋值时,哪怕写了 $fillable = ['name', 'email'],只要请求多传一个 token 字段,就直接报错中断。
- 必须显式调用
allowField(true)才能绕过字段白名单校验(仅限当前操作) -
$fillable仅对create()、save()等批量赋值方法起作用;单字段赋值如$model->name = $input['name']不受保护 - ThinkPHP 5.1 的
$allowField属性在 6+ 中已废弃,改用方法链式调用
如何让旧代码兼容新版本的字段过滤逻辑
升级后最省事的过渡方案不是关掉保护,而是补全白名单或明确放行——否则所有表单提交都可能失败。
- 检查每个模型的
$fillable,把实际需要接收的字段(包括隐藏域、前端生成的 token、时间戳等)全部列进去 - 对动态字段场景(如用户自定义字段),改用
allowField(['name', 'email', ...])动态传入白名单数组 - 绝对不要用
allowField(true)全局放开,它会绕过整个字段过滤机制,等于退回到 ThinkPHP 5.0 的不安全模式 - 注意:关联写入(
with())不受$fillable控制,需单独在关联模型中配置
protected $validate 和 $fillable 谁先执行
字段过滤($fillable)发生在数据验证($validate)之前。如果字段没进白名单,连验证器都不会触发。
- 错误示范:
$validate = ['status' => 'require|in:0,1'],但status不在$fillable中 → 直接报字段不存在,验证规则压根不跑 - 调试技巧:在模型中加日志,确认
data()方法返回前是否已被截断 - ThinkPHP 6.1+ 支持
validateScene()场景验证,但字段白名单仍以$fillable为准,不随场景变化
MySQL 严格模式下字段类型不匹配也会触发过滤误判
当数据库字段是 TINYINT(1),而 PHP 传入字符串 "true",ThinkPHP 6+ 的自动类型转换可能失败,导致该字段被当作「非法字段」丢弃,表面看像 $fillable 失效。
- 典型错误信息:
The field is_deleted is not exists in table user,其实字段存在,只是类型转换失败后被过滤了 - 解决方案:在模型中重写
type()方法,或使用setAttrIsDeletedAttr()显式转换 - 建议统一用
tinyint(1)存布尔值,并在接收时强制转为(int)或boolval()
$fillable 的防护范围内。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











