彻底关闭 laravel 时间戳需三步:模型设 $timestamps = false,迁移删字段并确认表结构无 created_at/updated_at,若字段必须保留则设 created_at = null 和 updated_at = null。

直接在模型里设 $timestamps = false 就能关掉,但光写这行代码常不够——数据库字段还在、迁移没删、表结构有 DEFAULT 约束,都会让 created_at 继续被写入 NULL 或当前时间。
怎么彻底关闭 timestamps(不只是“不自动填”)
关掉 Eloquent 的自动填充只是第一步。真正“关闭”意味着插入/更新时字段完全不参与 SQL 操作,不传值、不占位、不触发数据库默认行为。
- 模型中必须加
public $timestamps = false; - 迁移文件里不能有
$table->timestamps();,已有字段需用dropColumn()显式删掉 - 检查表结构:
DESCRIBE your_table;确认created_at和updated_at字段已不存在;若还存在,且带DEFAULT CURRENT_TIMESTAMP或NOT NULL,即使模型关了,MySQL 仍可能按默认规则填值 - 如果字段必须保留(比如 legacy 表),但不想被 Laravel 触碰,就别删字段,改用
const CREATED_AT = null; const UPDATED_AT = null;—— 这样字段名被设为空,Eloquent 不会尝试读/写它们,但你仍可手动赋值
DB::table() 更新时 updated_at 不变?不是 bug,是设计
用 DB::table('posts')->where('id', 1)->update(['title' => 'new']) 时,updated_at 确实不会变。这不是漏功能,而是查询构造器根本不走 Eloquent 生命周期,自然跳过 updateTimestamps() 调用。
- 想保持时间戳更新?必须走模型:用
Post::where('id', 1)->update(['title' => 'new'])(它会实例化模型并触发时间戳逻辑) - 必须用
DB::table()(比如批量更新百万行)?就手动补上时间字段:DB::table('posts')->where('id', 1)->update(['title' => 'new', 'updated_at' => now()]) - 注意时区:
now()返回 PHP 默认时区时间,而 MySQL 可能用系统时区。建议统一用Carbon::now('UTC'),并在数据库连接配置中设'timezone' => '+00:00'
save() 时临时禁用 updated_at 的正确写法
想只更新几个字段、不动 updated_at?别传参数给 save(),那没用。
- 错误写法:
$post->title = 'new'; $post->save(['timestamps' => false]);——save()根本不认这个键,源码里只处理touch - 正确写法:
$post->timestamps = false; $post->title = 'new'; $post->save(); - Laravel 10.29+ 可用
$post->updateQuietly(['title' => 'new']),它跳过事件和时间戳,比手动开关$timestamps更干净 - 如果只是单次操作,记得之后恢复:
$post->timestamps = true;,否则后续save()都不会更新时间戳
最易忽略的点:字段名自定义后,只改 UPDATED_AT 不够,CREATED_AT 也得显式设为 null 或对应字段,否则 create() 还会试图写 created_at,报错或静默失败都可能发生。











