Yii模型update()需显式传入字段白名单数组才能只更新指定字段,save()默认更新所有已加载非空属性;推荐用scenarios()定义不同操作的字段集配合save(false),关联字段须单独处理。

Yii模型update()时只更新指定字段,必须显式传入第二个参数
Yii的save()默认会把模型所有已加载的非空属性都写入数据库,想只改几个字段,不能靠“不赋值”来规避——只要属性在当前场景(scenario)里被验证通过,就可能被更新。真正可控的方式是调用update()并明确列出要更新的字段名数组。
-
update()第一个参数是是否运行验证(true或false),第二个参数才是字段白名单,例如:$model->update(false, ['status', 'updated_at']) - 如果用
save(false),它仍会尝试写入所有当前scenarios()中声明的属性,不是你“没碰过的字段就安全” - 字段名必须和数据库列名一致,不能写虚拟属性或getter名(如
full_name) - 时间戳类字段(如
updated_at)建议手动赋值再进白名单,避免依赖behaviors自动填充导致意外覆盖
用scenario控制update范围比硬编码字段更可持续
当不同操作需要更新不同字段组合(比如“审核通过”只改status和approved_by,“编辑资料”只改email和phone),硬编码update()字段列表容易散落在各处、难以维护。这时应定义专用scenario,在scenarios()里声明字段集,再配合save()使用。
- 在模型中定义:
public function scenarios() { return ['audit' => ['status', 'approved_by'], 'profile' => ['email', 'phone']]; } - 设置后调用:
$model->setScenario('audit'); $model->save(false);——此时即使$model->email有值,也不会被更新 - 注意:scenario只影响
rules()校验和save()写入字段,对update()无约束;update()始终以你传入的字段数组为准 - 别在
scenarios()里漏掉必填字段(如主键),否则save()可能因缺少WHERE条件而失败
关联字段更新要小心,别让update()误触外键
如果你的模型有hasOne或hasMany关联,并且在update()字段列表里写了关联对象的属性(比如profile.name),Yii不会自动级联更新——它只会尝试更新当前模型的profile_id字段(如果存在),或直接报错。关联数据必须单独处理。
- 错误写法:
$user->update(false, ['profile.name']);→ PHP会报Trying to get property 'name' of non-object或静默忽略 - 正确做法:先查出关联模型,再单独
save(),例如:$user->profile->name = 'new'; $user->profile->save(false); - 如果想原子化更新,得用事务包裹两个
save(),并在主模型afterSave()里做关联同步,而不是指望update()跨表 - 外键字段(如
category_id)可以放心放进update()白名单,但要确保值合法,否则数据库会报Integrity constraint violation
filterAttribute()不是为update设计的,别滥用
有些开发者看到filterInput()或第三方扩展里的filterAttribute()方法,以为能用来“过滤掉不想更新的字段”。这是误解——Yii核心没有filterAttribute()这个机制。所谓过滤,只能发生在数据进入模型之前(如load()时用except参数),或写入数据库之前(用update()字段白名单或scenarios())。
-
$model->load($data, '', ['except' => ['password_hash']])可防止敏感字段被赋值,但它不阻止后续手动$model->password_hash = 'xxx'再save() - 不要在
beforeSave()里unset($this->password_hash)——这只能防止该字段被写入,但无法解决并发写入冲突或日志记录残留问题 - 最干净的防线是:前端不传、控制器不接收、模型不暴露、更新时白名单兜底
update()字段白名单和scenarios()配合用得最多;关联更新永远单独走,不混进主模型update();任何试图靠“拦截”或“后置清除”来过滤字段的做法,都会在复杂流程中露出破绽。


















