ThinkPHP模型create_time为空是因自动时间戳配置、字段名映射、调用方式三者未对齐:需同时设protected $autoWriteTimestamp = true、$createTime = 'create_time'、$updateTime = 'update_time',且字段存在、类型匹配、未被allowField过滤,仅模型save()有效。

ThinkPHP模型中create_time字段始终为空,不是数据库没建好,也不是代码漏写了save(),而是模型配置、字段名映射、调用方式三者之间存在错位——只要其中一环没对齐,时间戳就静默失效。
确认模型是否真正启用了自动时间戳
TP6默认完全关闭自动时间戳,只设$autoWriteTimestamp = true还不够,必须同时声明字段名;TP5.1虽默认开启,但只认create_time/update_time,自定义字段名必须显式配置。
在模型类(如app\model\User.php)中,添加以下三行,缺一不可:
protected $autoWriteTimestamp = true;
立即学习“PHP免费学习笔记(深入)”;
protected $createTime = 'create_time';
protected $updateTime = 'update_time';
注意:这三行必须全部是protected,不能是public或不写修饰符;若数据库字段名为created_at,则第二行必须改为'created_at',且大小写、下划线必须与数据库列名【完全一致】。
检查数据库字段是否存在且类型匹配
自动写入的前提是字段真实存在。如果表里压根没有create_time字段,或字段名为add_time却没在模型里配置$createTime = 'add_time',框架会直接跳过写入,不报错也不提示。
执行 SQL 查看字段定义:DESCRIBE user;,确认字段存在且类型为 DATETIME、TIMESTAMP 或 INT。
若字段类型为 INT,必须额外加类型声明:protected $type = ['create_time' => 'integer'];。否则框架尝试把Carbon对象转成字符串再存进 INT 字段,结果写入 0 或触发类型错误。
验证是否被 allowField 过滤掉
这是最隐蔽的坑:用了allowField(true)却忘了时间字段不在白名单里,或者传了['field' => ['id', 'name']]这种显式限制,create_time会被直接丢弃。
方法一:临时去掉所有字段限制,用最简方式测试
$user = new User();
$user->name = 'test';
$user->save(); // 不传任何 field 参数
方法二:显式把时间字段加入白名单
$user->allowField(['id', 'name', 'create_time'])->save($data);
方法三:改用data()->save()并确保数据数组不含手动赋值的时间字段——【一旦$data里带了'create_time' => time(),自动写入逻辑立刻被覆盖】。
确保调用的是模型 save() 而非 Db::insert()
自动时间戳只在模型层生命周期钩子中触发,Db 类操作完全绕过模型逻辑。
第一步:确认控制器中调用的是模型实例的save()
正确:$user->save(['name' => 'xxx']); 或 User::create(['name' => 'xxx']);
错误:Db::name('user')->insert(['name' => 'xxx']); —— 这种写法无论模型怎么配,create_time都不会自动写入。
第二步:检查是否混用了模型和 Db 操作。例如先用User::find(1)查出数据,再用Db::name('user')->update()更新,此时update_time也不会刷新。
第三步:调试时在save()前加一行:dump($user->getData());,观察输出数组里是否有create_time键及其值。没有,说明前面某步已过滤或未触发赋值。



















