Laravel数据库迁移中,如何使用DB::raw设置字段默认当前时间值?
- 内容介绍
- 文章标签
- 相关推荐
本文共计758个文字,预计阅读时间需要4分钟。
MySQL 5.6.5 或 PostgreSQL 才能使用 DB::raw() 给字段设置默认时间表达式,Laravel 迁移里直接写 + useCurrent() 或 useCurrentOnUpdate() 更安全、更易移植。
MySQL 中用 DB::raw('CURRENT_TIMESTAMP') 设默认值会失败
很多人在迁移里这么写:
Schema::table('posts', function (Blueprint $table) { $table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'))->change(); });
这会在 MySQL 5.6.5 以下或严格模式下报错:SQLSTATE[HY000]: General error: 1067 Invalid default value for 'created_at'。原因:MySQL 要求 TIMESTAMP / DATETIME 的默认表达式必须是常量(如 CURRENT_TIMESTAMP),但 Laravel 的 DB::raw() 在 default() 中不会被识别为“字面量”,而是当成字符串值插入,最终生成类似 DEFAULT 'CURRENT_TIMESTAMP' 的 SQL —— 带引号就失效了。
- MySQL 5.6.5+ 支持
CURRENT_TIMESTAMP作为默认值,但必须不带引号 - Laravel 8+ 的
useCurrent()会生成无引号的DEFAULT CURRENT_TIMESTAMP - 如果硬要用
DB::raw(),只能在DB::statement()里手写原生 SQL,但失去迁移可逆性
useCurrent() 和 useCurrentOnUpdate() 是正解
它们专为时间戳默认值设计,适配不同数据库驱动,且自动生成合规 SQL:
Schema::table('posts', function (Blueprint $table) { $table->timestamp('created_at')->useCurrent()->change(); $table->timestamp('updated_at')->useCurrentOnUpdate()->change(); });
注意几个关键点:
-
useCurrent()对应CURRENT_TIMESTAMP,仅用于created_at类字段 -
useCurrentOnUpdate()生成CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,适合updated_at - 这两个方法只对
timestamp()和datetime()字段有效,date()不行 - 如果字段已存在,必须配合
->change(),且需启用doctrine/dbal
PostgreSQL 用户别用 DB::raw() 模拟默认时间
PostgreSQL 不支持 CURRENT_TIMESTAMP 作为列默认值的简写语法(它要求显式函数调用)。但 Laravel 的 useCurrent() 会自动转成 now(),而 useCurrentOnUpdate() 在 PG 中无效(PG 无原生 ON UPDATE 支持,得靠触发器)。所以:
- PG 下
useCurrent()安全可用,生成DEFAULT now() - 不要手动写
default(DB::raw('now()'))—— 看似可行,但DB::raw()会绕过 Laravel 的类型推断,导致后续change()失败 - 需要自动更新时间?用模型的
updating()事件或数据库触发器,别强求迁移层解决
真正容易被忽略的是数据库版本和方言差异:同一段迁移代码,在 MySQL 5.6 和 8.0、SQLite 和 PG 下行为可能完全不同。useCurrent() 是 Laravel 封装的兼容层,而 DB::raw() 是裸奔 —— 除非你明确知道目标环境并愿意为每个平台单独维护 SQL,否则别碰。
本文共计758个文字,预计阅读时间需要4分钟。
MySQL 5.6.5 或 PostgreSQL 才能使用 DB::raw() 给字段设置默认时间表达式,Laravel 迁移里直接写 + useCurrent() 或 useCurrentOnUpdate() 更安全、更易移植。
MySQL 中用 DB::raw('CURRENT_TIMESTAMP') 设默认值会失败
很多人在迁移里这么写:
Schema::table('posts', function (Blueprint $table) { $table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'))->change(); });
这会在 MySQL 5.6.5 以下或严格模式下报错:SQLSTATE[HY000]: General error: 1067 Invalid default value for 'created_at'。原因:MySQL 要求 TIMESTAMP / DATETIME 的默认表达式必须是常量(如 CURRENT_TIMESTAMP),但 Laravel 的 DB::raw() 在 default() 中不会被识别为“字面量”,而是当成字符串值插入,最终生成类似 DEFAULT 'CURRENT_TIMESTAMP' 的 SQL —— 带引号就失效了。
- MySQL 5.6.5+ 支持
CURRENT_TIMESTAMP作为默认值,但必须不带引号 - Laravel 8+ 的
useCurrent()会生成无引号的DEFAULT CURRENT_TIMESTAMP - 如果硬要用
DB::raw(),只能在DB::statement()里手写原生 SQL,但失去迁移可逆性
useCurrent() 和 useCurrentOnUpdate() 是正解
它们专为时间戳默认值设计,适配不同数据库驱动,且自动生成合规 SQL:
Schema::table('posts', function (Blueprint $table) { $table->timestamp('created_at')->useCurrent()->change(); $table->timestamp('updated_at')->useCurrentOnUpdate()->change(); });
注意几个关键点:
-
useCurrent()对应CURRENT_TIMESTAMP,仅用于created_at类字段 -
useCurrentOnUpdate()生成CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,适合updated_at - 这两个方法只对
timestamp()和datetime()字段有效,date()不行 - 如果字段已存在,必须配合
->change(),且需启用doctrine/dbal
PostgreSQL 用户别用 DB::raw() 模拟默认时间
PostgreSQL 不支持 CURRENT_TIMESTAMP 作为列默认值的简写语法(它要求显式函数调用)。但 Laravel 的 useCurrent() 会自动转成 now(),而 useCurrentOnUpdate() 在 PG 中无效(PG 无原生 ON UPDATE 支持,得靠触发器)。所以:
- PG 下
useCurrent()安全可用,生成DEFAULT now() - 不要手动写
default(DB::raw('now()'))—— 看似可行,但DB::raw()会绕过 Laravel 的类型推断,导致后续change()失败 - 需要自动更新时间?用模型的
updating()事件或数据库触发器,别强求迁移层解决
真正容易被忽略的是数据库版本和方言差异:同一段迁移代码,在 MySQL 5.6 和 8.0、SQLite 和 PG 下行为可能完全不同。useCurrent() 是 Laravel 封装的兼容层,而 DB::raw() 是裸奔 —— 除非你明确知道目标环境并愿意为每个平台单独维护 SQL,否则别碰。

