useCurrent() 仅对 timestamp 类型生效且要求非 NULL 约束,MySQL 严格模式与类型不匹配会导致设为 NULL;需搭配 useCurrentOnUpdate() 并统一用 timestamp 类型。

Laravel 迁移中 useCurrent() 为什么没生效?
常见现象是:字段设了 useCurrent(),但插入数据时值仍是 NULL 或 0000-00-00 00:00:00。根本原因不是写法错,而是 MySQL 严格模式和字段类型不匹配。
-
useCurrent()只对timestamp类型生效(datetime不支持),且要求该字段不能有NULL约束(除非显式允许并设默认值) - MySQL 5.7+ 默认开启严格模式 +
NO_ZERO_DATE,遇到非法时间会报错或转成NULL,导致看似“没生效” - Laravel 9+ 对
useCurrent()的底层生成逻辑已改:它只加DEFAULT CURRENT_TIMESTAMP,不自动加ON UPDATE CURRENT_TIMESTAMP - 若想同时支持创建和更新时间,得分开写:
$table->timestamp('created_at')->useCurrent()+$table->timestamp('updated_at')->useCurrentOnUpdate()
useCurrent() 和 useCurrentOnUpdate() 怎么配对用?
这是 Laravel 8.34+ 引入的明确拆分,替代旧版模糊的 useCurrent() 行为。两者语义清晰,但必须搭配正确的字段类型和约束。
-
useCurrent()→ 仅设置DEFAULT CURRENT_TIMESTAMP,适用于created_at -
useCurrentOnUpdate()→ 仅设置ON UPDATE CURRENT_TIMESTAMP,适用于updated_at - 两个都用在同一个字段上(如
updated_at),Laravel 会生成DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,但注意:MySQL 要求该字段必须是timestamp,且不能是主键或有NOT NULL冲突 - 示例迁移片段:
$table->timestamp('created_at')->useCurrent();<br>$table->timestamp('updated_at')->useCurrentOnUpdate();
MySQL 版本和 SQL_MODE 影响 useCurrent() 的实际行为
同一段迁移代码,在不同环境可能表现完全不同——不是 Laravel 问题,是底层 MySQL 的锅。
- MySQL 5.6:支持
timestamp的CURRENT_TIMESTAMP默认值,但不支持多个timestamp字段都设默认值(仅第一个可以) - MySQL 5.7+:放宽限制,但受
SQL_MODE控制。若含STRICT_TRANS_TABLES或NO_ZERO_DATE,而字段定义为datetime NOT NULL却没给默认值,迁移会直接失败 - 检查当前模式:
SELECT @@sql_mode;;临时绕过(仅开发):SET sql_mode = '';,但上线前必须修复字段定义 - 稳妥做法:统一用
timestamp类型 + 显式useCurrent(),避免依赖 MySQL 自动转换
模型里 $dates 或 $casts 会影响迁移结果吗?
完全不影响。迁移是数据库结构操作,$dates 和 $casts 是 Eloquent 层的数据处理逻辑,两者不在同一阶段执行。
-
$dates = ['created_at', 'updated_at']只影响模型取值时是否转成 Carbon 实例,对建表无任何作用 -
$casts = ['created_at' => 'datetime']同理,只在属性访问、序列化时起效 - 真正决定数据库默认值的,只有迁移文件里的
useCurrent()和字段类型声明 - 容易混淆的点:如果迁移写了
useCurrent(),但模型又在creating事件里手动设了created_at,PHP 层赋值会覆盖数据库默认值——这不是useCurrent()失效,是业务逻辑干预了
Laravel 的 useCurrent() 看似简单,但实际效果高度依赖 MySQL 版本、SQL_MODE、字段类型三者配合。最容易被忽略的是:它不解决“字段为空时 PHP 层要不要填”,只管“数据库插入时缺值怎么补”。别指望它兜底业务层的时间赋值逻辑。











