Yii 2.x+ 已移除 defaultScope,须重写模型 find() 方法并用 andWhere() 添加全局条件,命名 scope 必须是返回 Query 的 public static 方法,行为无法自动注入查询链。

defaultScope 在 Yii 1.x 中有效,Yii 2.x+ 已移除
Yii 2.0 开始,defaultScope 方法被彻底废弃,模型类里定义它不会生效,也不会报错——这是最常踩的坑。官方改用 find() 静态方法 + where() 或 andWhere() 搭配 BaseActiveRecord::find() 的重写来实现类似效果。
如果你在 Yii 2.1+ 项目里看到某处写了 defaultScope() 却发现软删除、租户 ID 过滤没起作用,大概率就是掉进这个兼容性陷阱了。
Yii 2.x 正确添加全局查询条件的方式
必须在模型的 find() 静态方法中显式追加条件,且要确保所有查询入口都走这个方法(比如 User::find()->one()、User::find()->all()),直接调用 findOne() 或 findAll() 不会触发。
- 重写模型的
find()方法,返回带默认条件的 Query 对象 - 用
parent::find()->andWhere(...)而不是where(),避免覆盖已有条件 - 对关联查询(如
with('profile'))也生效,但要注意关联表字段不能直接在主模型andWhere里引用 - 若需临时取消全局条件,用
User::find()->withoutDefaultScope()->one()(需自行实现该方法)
public static function find()
{
return parent::find()->andWhere(['status' => self::STATUS_ACTIVE]);
}
为什么不用行为(Behavior)统一处理 soft-delete?
虽然可以写个 SoftDeleteBehavior 并 attach 到模型上,但它无法自动注入到每个 find() 调用中——行为不参与查询构建过程,只响应事件或扩展实例方法。你仍然得在 find() 里手动调用行为的过滤逻辑,反而增加耦合和出错概率。
更实际的做法是:把通用过滤抽成静态方法,在 find() 里复用;或者用基类模型统一实现,子类按需覆写。
- 行为适合做数据保存前后的钩子(如自动设
updated_at),不适合改查询结构 - 全局条件本质是 Query 层的事,绕不开重写
find() - 多个全局条件叠加时(如租户 + 状态 + 可见范围),全堆在
find()里比分散在行为里更容易调试和测试
注意 scope 和 defaultScope 的命名混淆
Yii 2.x 支持 named scopes(即 scenarios?不,是 scope 方法,如 active()),但它们和已废弃的 defaultScope 完全无关。有人误以为定义一个叫 defaultScope() 的普通方法就能生效,其实这只是个无意义的实例方法,对查询零影响。
- 合法的命名 scope 必须是 public static 方法,返回
$this(Query 对象) - 像
public static function deleted() { return self::find()->where(['deleted' => 1]); }是有效的 scope - 但
public function defaultScope() { ... }或public static function defaultScope() { ... }都不会被框架识别 - 错误信息不会出现,查不到数据时才意识到条件根本没加进去
find() 重写,或者误信某个“自动生效”的命名约定,后面排查起来都得翻半天日志。










