Laravel 10 的 $table->timestamps() 在 MySQL 5.7+ 严格模式下报错,根本原因是 MySQL 限制一个表仅允许一个 TIMESTAMP 字段使用 CURRENT_TIMESTAMP(不能同时用于 DEFAULT 和 ON UPDATE),而 Laravel 默认为 created_at 和 updated_at 均配置 useCurrent(),触发冲突;解决方案包括改用 datetime 类型、显式拆分 useCurrent() 与 useCurrentOnUpdate()、或在开发环境临时关闭 strict 模式(生产禁用)。

直接说结论:Laravel 10 迁移中 timestamps() 报错,90% 是 MySQL 严格模式(STRICT_TRANS_TABLES)和 TIMESTAMP 字段默认值规则冲突导致的,不是代码写错了,而是数据库配置和 Laravel 默认行为不匹配。
为什么 $table->timestamps() 在 MySQL 5.7+ 会报错?
MySQL 5.7 开启严格模式后,一个表只允许**一个** TIMESTAMP 字段带 CURRENT_TIMESTAMP,且不能同时用于 DEFAULT 和 ON UPDATE。而 Laravel 10 的 $table->timestamps() 底层生成的是:
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->useCurrentOnUpdate()->useCurrent();
这会让两个字段都尝试用 CURRENT_TIMESTAMP,触发 MySQL 的校验失败。
常见错误信息包括:
SQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default value for 'created_at'Incorrect table definition; there can be only one timestamp column with current_timestamp in default or on update clause
迁移里怎么安全定义时间字段?
别硬扛默认行为,按目标 MySQL 版本选方案:
- MySQL 5.7+ 且无法改配置 → 改用
datetime类型:$table->datetime('created_at')->nullable();$table->datetime('updated_at')->nullable();
然后在模型里手动赋值或靠业务逻辑控制 - 想保留数据库自动填充 → 显式拆开写,默认只给
created_at设useCurrent(),updated_at只设useCurrentOnUpdate():$table->timestamp('created_at')->useCurrent();$table->timestamp('updated_at')->useCurrentOnUpdate(); - 开发环境可接受宽松行为 → 在
config/database.php的 MySQL 配置里关严格模式:'strict' => false,
但**生产环境严禁这样做**
phpMyAdmin 手动改字段时默认值怎么填?
绕过迁移、直接在 phpMyAdmin「结构 → 更改」界面操作,填错默认值会静默失败或报错,必须严格对应类型:
-
created_at字段(TIMESTAMP类型):
默认值选CURRENT_TIMESTAMP,勾选「自动更新」 -
updated_at字段(TIMESTAMP类型):
默认值留空,但勾选「自动更新」并设为CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -
DATETIME类型字段:
**不能**填CURRENT_TIMESTAMP作默认值,否则报错;如需默认时间,得靠应用层写入
doctrine/dbal 不会帮你兜底,默认值合法性得自己盯
即使你装了 doctrine/dbal 并用了 ->change(),它也**不会校验**你写的默认值是否合法,只会原样生成 ALTER TABLE 语句。比如你在迁移里写:
$table->timestamp('updated_at')->useCurrentOnUpdate()->useCurrent()->change();
它照样发给 MySQL,而 MySQL 会在执行时才报错。所以每次改时间字段前,务必确认当前 MySQL 的 sql_mode 和字段类型是否兼容。
最易被忽略的一点:改完迁移后,别只跑 php artisan migrate 就以为完事——要进 phpMyAdmin 或用 SHOW CREATE TABLE 确认实际建出来的字段定义,尤其是 DEFAULT 和 ON UPDATE 子句是否如你所愿。


















