ThinkPHP中模型字段如何实现自动填充创建和更新时间戳?
- 内容介绍
- 文章标签
- 相关推荐
本文共计881个文字,预计阅读时间需要4分钟。
ThinkPHP+6+的模型时间戳是opt-in机制,默认不启用。需要显式开启时间戳功能,并且确保字段名、类型、配置三者对应正确。
- 必须在模型类中设置
protected $autoWriteTimestamp = true;(或具体指定'datetime'/'int') - 字段名默认是
create_time和update_time,若数据库用的是createTime,需同步改protected $createTime = 'createTime';和protected $updateTime = 'updateTime'; -
createTime只在save()且主键为空(即新增)时触发;updateTime在save()且主键存在时更新——但手动调用update()静态方法时,默认不走模型时间戳逻辑,得加['force' => true]
为什么 save(['id' => 1, 'name' => 'xxx']) 没更新 updateTime?
因为 ThinkPHP 默认只对「模型实例的属性变更」做时间戳感知,直接传数组给 save() 会跳过属性赋值流程,导致 updateTime 不被识别为待更新字段。
- 正确做法:先查出模型实例,再改属性,再
save():$user = User::find(1);<br>$user->name = 'xxx';<br>$user->save();
- 或强制启用时间戳:用
User::update(['id' => 1, 'name' => 'xxx'], [], ['force' => true]),但注意这会绕过模型事件和验证 - 如果用
Db::name('user')->update(...),时间戳完全不生效——这是原生查询,模型层逻辑根本不参与
createTime 被重复覆盖,比如编辑时也重写了创建时间?
常见于字段名冲突或配置松动:当 $createTime 和 $updateTime 指向同一字段,或未关闭自动写入逻辑,就容易误刷。
- 确认两者指向不同字段:
protected $createTime = 'create_time'; protected $updateTime = 'update_time'; - 若不需要自动写入
createTime(比如从 Excel 导入已有时间),可设protected $autoWriteTimestamp = ['update_time' => 'datetime'];,只管更新时间 - 数据库字段类型要匹配:用
datetime就配'datetime',用int时间戳就配'int',否则可能写入 0 或报错DateTime::__construct(): Failed to parse time string
MySQL 5.6+ 的 DEFAULT CURRENT_TIMESTAMP 和 ThinkPHP 时间戳能共存吗?
能,但容易互相干扰——尤其在批量插入或忽略字段时,数据库默认值和 PHP 层填充会竞争。
立即学习“PHP免费学习笔记(深入)”;
- 推荐只留一端控制:要么全交给 ThinkPHP(关掉数据库默认值,字段设为
NULL),要么全交给数据库(模型里关掉$autoWriteTimestamp,靠DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) - 若两边都开,
createTime可能被 PHP 写一次、数据库又写一次;更糟的是,某些 MySQL 配置下ON UPDATE会对INSERT也触发,导致创建时间被刷成当前时间 - 调试时看 SQL 日志最准:
think.log里搜INSERT INTO user,确认字段是否含create_time值
本文共计881个文字,预计阅读时间需要4分钟。
ThinkPHP+6+的模型时间戳是opt-in机制,默认不启用。需要显式开启时间戳功能,并且确保字段名、类型、配置三者对应正确。
- 必须在模型类中设置
protected $autoWriteTimestamp = true;(或具体指定'datetime'/'int') - 字段名默认是
create_time和update_time,若数据库用的是createTime,需同步改protected $createTime = 'createTime';和protected $updateTime = 'updateTime'; -
createTime只在save()且主键为空(即新增)时触发;updateTime在save()且主键存在时更新——但手动调用update()静态方法时,默认不走模型时间戳逻辑,得加['force' => true]
为什么 save(['id' => 1, 'name' => 'xxx']) 没更新 updateTime?
因为 ThinkPHP 默认只对「模型实例的属性变更」做时间戳感知,直接传数组给 save() 会跳过属性赋值流程,导致 updateTime 不被识别为待更新字段。
- 正确做法:先查出模型实例,再改属性,再
save():$user = User::find(1);<br>$user->name = 'xxx';<br>$user->save();
- 或强制启用时间戳:用
User::update(['id' => 1, 'name' => 'xxx'], [], ['force' => true]),但注意这会绕过模型事件和验证 - 如果用
Db::name('user')->update(...),时间戳完全不生效——这是原生查询,模型层逻辑根本不参与
createTime 被重复覆盖,比如编辑时也重写了创建时间?
常见于字段名冲突或配置松动:当 $createTime 和 $updateTime 指向同一字段,或未关闭自动写入逻辑,就容易误刷。
- 确认两者指向不同字段:
protected $createTime = 'create_time'; protected $updateTime = 'update_time'; - 若不需要自动写入
createTime(比如从 Excel 导入已有时间),可设protected $autoWriteTimestamp = ['update_time' => 'datetime'];,只管更新时间 - 数据库字段类型要匹配:用
datetime就配'datetime',用int时间戳就配'int',否则可能写入 0 或报错DateTime::__construct(): Failed to parse time string
MySQL 5.6+ 的 DEFAULT CURRENT_TIMESTAMP 和 ThinkPHP 时间戳能共存吗?
能,但容易互相干扰——尤其在批量插入或忽略字段时,数据库默认值和 PHP 层填充会竞争。
立即学习“PHP免费学习笔记(深入)”;
- 推荐只留一端控制:要么全交给 ThinkPHP(关掉数据库默认值,字段设为
NULL),要么全交给数据库(模型里关掉$autoWriteTimestamp,靠DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) - 若两边都开,
createTime可能被 PHP 写一次、数据库又写一次;更糟的是,某些 MySQL 配置下ON UPDATE会对INSERT也触发,导致创建时间被刷成当前时间 - 调试时看 SQL 日志最准:
think.log里搜INSERT INTO user,确认字段是否含create_time值

