yii2需手动实现脏检查:在afterfind()中保存原始值到$_original,再通过haschanged()严格比较;getoldattribute()不可靠,因它随save()或refresh()更新且不跟踪自动字段;updateall()需外部传参判断变更。

Yii2 没有内置的 isDirty() 或 getDirty() 方法——这是 Laravel 的机制,直接套用会报错;想判断字段是否真被改过,必须手动比对原始值和当前值。
Yii2 中怎么知道某个字段被修改了
Yii2 不维护 $original 快照,所以不能像 Laravel 那样调用 isDirty('name')。核心思路是:在加载数据后、保存前,显式记录原始值,再对比。
- 最稳妥的做法是在
afterFind()里保存原始状态:$this->_original = $this->getAttributes(); - 然后自定义一个方法,比如
hasChanged($attribute),内部做return $this->$attribute !== $this->_original[$attribute]; - 注意用
!==(严格比较),避免字符串'0'和整数0被误判为相同 - 如果字段启用了
$casts(如'status' => 'integer'),要确保getAttributes()返回的是转换后的值,否则原始值类型可能不一致
为什么 $model->getOldAttribute() 不总是可靠
getOldAttribute() 看似是 Yii2 的“原始值”接口,但它只在从数据库查出来后、首次修改前有效;一旦你调用 save() 成功,或执行 refresh(),它就同步更新为新值,不再反映“上一次入库时的状态”。
- 它不适用于跨请求判断(比如编辑页提交时,想对比用户上次保存的内容)
- 它对批量赋值(
$model->attributes = $data)后立即调用,可能返回空或旧快照,因为afterFind()没触发 - 软删除字段
deleted_at的变更不会体现在getOldAttribute('deleted_at')里——它只跟踪主动 set 的属性,不包含行为自动注入的字段
在 updateAll() 和 save() 之间如何统一判断逻辑
如果你既用 $model->save() 又用 User::updateAll(),就不能依赖模型实例上的状态,得把判断逻辑下沉到 DAO 层或服务层。
- 对
save()场景,走上面说的afterFind + hasChanged()方案 - 对
updateAll(),压根没模型实例,只能靠外部传参明确告诉系统“这次要更新哪些字段”,例如:updateUserStatus($ids, ['status' => 2]) - 若需审计变更,建议统一收口到一个 Service 方法,在里面先查旧值(
User::findAll($ids)),再比对传入的新值,最后决定调save()还是updateAll() - 别试图给
updateAll()套脏检查——它绕过模型生命周期,连事件都不触发,强行加只会让逻辑分裂
真正容易被忽略的是时间戳字段(updated_at)和软删除字段(deleted_at):它们常被框架自动更新,但业务上你可能并不认为这是“用户修改”。做变更判断时,要么显式过滤掉这些字段,要么在 hasChanged() 里加白名单,否则通知或日志里全是噪音。











