updated_at未生效最常见的原因是数据库字段名或类型不匹配;必须为datetime/timestamp类型,不能设NOT NULL DEFAULT '0000-00-00 00:00:00',且需确保使用Eloquent操作而非DB::table()直连更新。

模型自动更新 updated_at 为什么没生效
最常见的原因是数据库字段名或类型不匹配。updated_at 字段必须是 datetime 或 timestamp 类型,且不能设为 NOT NULL DEFAULT '0000-00-00 00:00:00'(MySQL 5.7+ 严格模式下会拒绝插入)。Laravel 默认只监听 save()、update()、touch() 等操作,直接用 DB::table()->update() 绕过 Eloquent 就不会触发。
- 检查迁移文件中是否用了
$table->timestamps(),它会生成created_at和updated_at - 如果字段名不是默认值,需在模型里显式声明:
const UPDATED_AT = 'modified_time'; - 手动更新时调用
$model->touch()可强制刷新updated_at
Laravel 关闭 timestamps 的两种方式及适用场景
关闭不是“禁用功能”,而是告诉 Eloquent:别管这俩字段。常见于日志表、中间表、或已存在 legacy 表结构且无法改字段名的场景。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 全局关闭:在模型中设置
public $timestamps = false; - 仅关闭某一个字段:用
const CREATED_AT = null;或const UPDATED_AT = null; - 注意:关闭后
create()不再自动写入created_at,也不会在update()时更新updated_at—— 这是预期行为,不是 bug
使用 DB::table() 更新时 timestamps 不更新的真相
因为 DB::table() 是查询构造器,完全绕过 Eloquent 模型生命周期,自然不会触发时间戳逻辑。这不是 Laravel 的缺陷,而是设计使然:它本就不该承担模型层职责。
- 想保持 timestamps 更新?必须走模型实例,比如
Post::where('id', 1)->update(['title' => 'new']) - 如果必须用
DB::table()(例如批量更新千万级数据),就手动加上时间字段:DB::table('posts')->where('id', 1)->update(['title' => 'new', 'updated_at' => now()]) - 注意时区:
now()返回的是 PHP 默认时区时间,而 MySQL 可能用系统时区,建议统一用 UTC 或显式指定:Carbon::now('UTC')
时间戳字段命名不一致导致的隐性问题
当数据库字段叫 last_modified,但模型里只写了 const UPDATED_AT = 'last_modified';,看起来能用,但容易漏掉另一个坑:Laravel 在执行 create() 时仍会尝试写入 created_at,除非你也设了 const CREATED_AT = null; 或对应字段名。
- 字段名不一致时,两个常量都要检查:
CREATED_AT和UPDATED_AT - 如果数据库没有
created_at字段,又没设CREATED_AT = null,create()会抛出SQLSTATE[HY000]: General error: 1364 Field doesn't have a default value - 迁移中用
$table->timestamps()生成的字段是不可配置名的,若需自定义,得手写$table->dateTime('modified_time')->nullable();

















