ThinkPHP 8.0 模型时间戳默认关闭,必须同时满足三要素:$autoWriteTimestamp = true(或合法字符串)、$createTime/$updateTime 显式声明且与数据库字段名完全一致、数据库对应字段真实存在,缺一不可。

ThinkPHP 8.0 的模型时间戳默认是关闭的,不配就不会生效;所谓“关不掉”或“不触发”,基本都是 $autoWriteTimestamp、$createTime/$updateTime、数据库字段三者没对齐。
为什么设了 $autoWriteTimestamp = true 还不写入?
因为 TP8.0 要求三要素全部到位:开关打开 + 字段名匹配 + 数据库字段存在。缺一不可。
-
$autoWriteTimestamp必须为true或'datetime'等合法类型字符串,不能是1、'on'或空字符串 -
$createTime和$updateTime必须显式声明,且值必须与数据库列名**完全一致**(含大小写、下划线),比如数据库是add_time,就不能写成addTime或create_time - 如果用了
allowField(true),确保时间字段在表结构中真实存在,否则会被过滤掉;若用allowField(['id', 'name']),必须手动把时间字段加进去 - 调试时可 dump
$model->getData(),确认add_time是否已生成并出现在数组里
save() 传数组不更新 $updateTime?这是设计行为
TP8.0 的时间戳只对「模型属性变更」敏感,直接 save(['id' => 1, 'name' => 'xxx']) 会跳过属性赋值流程,导致 $updateTime 不被识别为待更新字段。
- 正确做法:先查实例,改属性,再
save()——$user = User::find(1); $user->name = 'xxx'; $user->save(); - 若必须用数组更新,得加
['force' => true]:User::update(['id' => 1, 'name' => 'xxx'], [], ['force' => true]),但注意这会绕过验证和事件 -
Db::table('user')->update()完全不走模型层,时间戳永远不生效
数据库 NOT NULL 报 “Column 'create_time' cannot be null” 怎么办?
这不是模型配置错了,是模型设了 $createTime = false 或未启用,但数据库字段强制非空且无默认值,MySQL 直接拦截。
立即学习“PHP免费学习笔记(深入)”;
- 最干净解法:
ALTER TABLE user MODIFY create_time DATETIME DEFAULT CURRENT_TIMESTAMP; - 次选:删掉该字段(如果业务真不需要)
- 临时补救:在
save()前手动赋值,如$data['create_time'] = date('Y-m-d H:i:s');,但这等于放弃自动机制 - 别指望
$autoWriteTimestamp = 'datetime'能绕过 NOT NULL——它只影响格式,不决定是否传值
字段名是 created_at/updated_at,但配置后还是 null?
常见于字段名拼写错位或大小写不一致。TP8.0 不做任何自动转换,必须字面量完全匹配。
- 检查数据库实际列名:是
created_at还是created_at(注意末尾下划线)、createdAt还是created_at - 模型中必须写成:
protected $createTime = 'created_at';,不能写成'createdAt'或'create_at' - 如果字段名带前缀如
_created_at,也要原样写:protected $createTime = '_created_at'; - TP8.0 不支持驼峰转下划线,也不忽略大小写,
Created_At和created_at是两个不同字段
真正容易被忽略的是 allowField 和数据库约束的组合效应:字段名对了、开关开了、模型也实例化了,但一行 allowField(true) 就能让时间戳彻底消失——因为它只校验“字段是否存在”,不关心“是不是时间戳”。



















