updateall() 不触发 activerecord 生命周期,模型属性不会自动更新,需显式调用 refresh() 或重新查询才能获取数据库最新值;save() 后同理,须调用 refresh() 同步数据库自动生成字段。

updateAll() 之后拿不到最新数据,因为不触发 ActiveRecord 生命周期
Yii2 的 updateAll() 是静态批量更新方法,直接生成 SQL 执行,跳过模型验证、事件、属性赋值等流程。调用后 $model 实例本身不会自动刷新,$model->attribute 仍是旧值。
- 如果你刚调用
MyModel::updateAll(['status' => 1], ['id' => 100]),再访问$model->status,它不会变成1—— 因为这个$model根本没参与这次更新 - 想拿到新数据,必须显式重新查库,不能依赖“更新完就自动同步”
- 适合场景:后台批量改状态、清缓存等无需返回结果的写操作;不适合“更新后立刻展示新值”的交互流
save() 后怎么确保拿到数据库真实值?用 refresh() 显式重载
对单条记录调用 save()(比如 $model->status = 2; $model->save();),虽然会触发事件和验证,但默认仍不会从数据库重新读取字段值——尤其是数据库有默认值、触发器、或时间戳自动更新(如 updated_at)时,PHP 对象里的值可能滞后。
- 安全做法是:保存成功后立即调用
$model->refresh() -
refresh()会执行一次SELECT,把数据库当前行所有字段覆盖到对象属性上 - 注意:如果模型开启了乐观锁(
optimisticLock()),refresh()也会更新version字段,避免后续save()冲突 - 示例:
$model = MyModel::findOne(100);<br>$model->name = 'new name';<br>if ($model->save()) {<br> $model->refresh(); // 此时 $model->updated_at、$model->version 等才是真实库值<br>}
想一步到位返回最新数据?封装成事务 + 查新值的组合操作
有些接口要求“更新并返回完整最新实体”,比如 RESTful API 的 PATCH /api/users/123。这时不能只靠 save() 或 updateAll(),得自己拼逻辑。
- 推荐用事务包裹更新 + 查询,防止中间被其他写操作干扰:
return \Yii::$app->db->transaction(function ($db) use ($id, $data) {<br> $rows = MyModel::updateAll($data, ['id' => $id]);<br> if (!$rows) return null;<br> return MyModel::findOne($id); // 此刻查到的就是刚更新后的最新快照<br>}); - 如果更新条件复杂(比如带 JOIN 或子查询),
updateAll()不支持,就得先findOne()→load()→save()→refresh(),否则无法保证时间戳等字段准确 - 注意:不要在
afterSave()里调用refresh(),这会导致无限递归(refresh 触发 afterFind,某些场景下又可能间接再 save)
别忽略数据库层面的自动行为
即使你用了 refresh() 或重新 findOne(),如果数据库字段定义了 ON UPDATE CURRENT_TIMESTAMP、触发器、或自增字段,PHP 对象的值才真正反映现实。但 Yii2 默认不会主动感知这些变化,除非你查库。
- 例如 MySQL 的
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP字段,在你save()时不设它,数据库会自动更新,但 PHP 对象里还是旧时间 —— 必须refresh()才能拿到 - PostgreSQL 的
DEFAULT now()或GENERATED ALWAYS AS列同理 - 所以“返回最新实体”的本质不是 Yii 的某个 magic 方法,而是“确保最后一次读库发生在写操作之后”











