最稳妥的方式是用 TimestampBehavior 而非手动 beforeSave,因其能覆盖批量更新等场景;失效主因是字段类型与值类型不匹配、字段名错误、rules() 中 required 验证拦截或 behaviors() 未正确继承。

Yii2 更新时间戳自动记录修改时间,最稳妥的方式是用 TimestampBehavior,而不是手动写 beforeSave —— 后者容易漏掉批量更新、事务回滚或 updateAll() 场景,导致 updated_at 静止不动。
TimestampBehavior 默认行为为什么没生效?
常见现象:加了行为,但保存模型后 updated_at 仍是 null 或旧值。原因通常是:
-
updated_at字段在数据库里是DATETIME类型,但行为默认用time()(整型时间戳),类型不匹配导致写入失败(MySQL 会静默转成0000-00-00 00:00:00) - 字段名拼错,比如写成
update_at或updated_time,行为不会报错,但也不起作用 -
rules()里对updated_at做了 required 验证,而行为是在验证之后才赋值,直接被拦住 - 模型继承自父类,但父类的
behaviors()没有return parent::behaviors() + [...],导致子类覆盖了行为
正确配置 TimestampBehavior 的三个关键点
必须同时满足这三点,updated_at 才能稳定更新:
- 数据库字段类型要匹配值类型:如果用
datetime,就别用time();改用new Expression('NOW()')或闭包返回date('Y-m-d H:i:s') - 显式声明事件和字段映射,不要依赖默认值:
'attributes' => [ ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'], ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'], ], -
updated_at不能出现在rules()的验证规则里,否则会被验证逻辑跳过赋值
updateAll() 或批量操作时 updated_at 不更新怎么办?
TimestampBehavior 只响应单条 AR 实例的 save(),对 User::updateAll()、createCommand()->update() 完全无效。此时必须手动处理:
- 用
Expression强制数据库层更新:User::updateAll(['updated_at' => new Expression('NOW()')], ['id' => $id]); - 如果业务强依赖统一时间源(比如避免数据库时钟偏差),可先在 PHP 层生成时间:
$now = date('Y-m-d H:i:s');User::updateAll(['updated_at' => $now], ['id' => $id]); - 切勿在
updateAll()后再调$model->save()补时间——这会多一次无意义查询,且破坏原子性
为什么有时 created_at 和 updated_at 都变了?
这是行为配置错误的典型表现:把 created_at 错写进 EVENT_BEFORE_UPDATE 的字段列表里,或者用了不带条件的通用配置。检查你的 attributes 数组,确保只有 updated_at 出现在 ActiveRecord::EVENT_BEFORE_UPDATE 下。另外,如果数据库表定义了 updated_at 的 ON UPDATE CURRENT_TIMESTAMP,PHP 层和数据库层会双重更新,可能引发时序混乱 —— 建议关掉数据库自动更新,全部交由 Yii 控制。


















