ThinkPHP 5.1+ 废弃自动完成机制,TP6 完全移除 $auto/$insert/$update,须改用修改器或事件绑定;三者为数组,仅在 save()/create() 中生效,where()->update() 绕过全部模型逻辑。

ThinkPHP 5.1+ 的自动完成机制已废弃,但 TP5.0 和 5.1 初期仍支持 $auto、$insert、$update 属性;TP6 完全移除,必须改用修改器或事件绑定。
TP5 中 $insert / $update / $auto 怎么配才生效
这三个属性是数组,定义字段名或 ['字段名' => '默认值'] 形式,只在 save() 或 create() 流程中触发(where()->update() 绕过所有模型逻辑):
-
$insert:仅新增时写入,比如protected $insert = ['status' => 1, 'create_time'];——create_time没给值,需靠修改器补 -
$update:仅更新时写入,如protected $update = ['update_time']; -
$auto:新增和更新都触发,适合通用字段(如统一转小写),但容易和表单提交字段冲突
注意:$insert = ['ip' => 'request()->ip()'] 这种写法无效——它不会执行函数调用,只会当字符串字面量存进去。要动态取值,必须配合修改器。
setXxxAttr 修改器怎么和自动完成配合用
自动完成本身不处理逻辑,只是“触发器”;真正干活的是修改器。字段出现在 $insert 或 $auto 里,且你定义了对应修改器,才会执行:
立即学习“PHP免费学习笔记(深入)”;
- 例如
protected $insert = ['create_time'];+public function setCreateTimeAttr($value) { return $value ?: date('Y-m-d H:i:s'); } - 若表单传了
create_time,$value 就是那个值;为空时才补默认值 - 密码加密也同理:
protected $auto = ['password'];+public function setPasswordAttr($value) { return md5($value); }
⚠️ 常见坑:表单同时提交 password 字段,又在 $auto 里写了 'password',会导致 md5() 被执行两次(一次在接收时,一次在 save 时)。
为什么 where()->update() 不触发自动完成
因为它是直接拼 SQL 执行的 Db 查询,完全绕过模型实例和生命周期钩子:
-
UserModel::where('id', 1)->update(['name' => 'foo'])→ 不走任何修改器、不触发onBeforeUpdate、不校验类型转换 - 想保持逻辑一致,就别用这个方法;改用
$user = UserModel::find(1); $user->name = 'foo'; $user->save(); - 批量更新且必须走模型逻辑?用
saveAll(),但它也不触发$update,只走修改器
简单说:只要没经过模型实例的赋值 → save 流程,自动完成就是摆设。
TP5 升级到 TP6 时最常踩的坑
不是报错,而是“静默失效”——旧配置还在,但什么也不发生:
- 删掉所有
$_auto、$auto、$filter等数组定义 - 把
$insert = ['create_time']改成修改器setCreateTimeAttr,并确保字段名拼写严格匹配(下划线/驼峰) - 事件回调如
onBeforeInsert必须显式绑定:self::event('before_insert', [static::class, 'onBeforeInsert']),否则不执行 - 别信老教程里“在控制器 new 模型后 save 就能触发事件”的说法——TP6 要求提前绑定,推荐写在
init()静态方法里
最麻烦的其实是时间字段:TP5 可能靠 $type = ['create_time' => 'datetime'] + $insert 混着用,TP6 必须拆成修改器 + 显式事件绑定,漏一个环节,create_time 就是 null。



















