Laravel全局作用域可统一注入业务查询约束:一、定义作用域类实现租户隔离;二、用闭包快速添加轻量约束;三、按序注册多作用域构建过滤链;四、精准禁用指定作用域;五、结合本地作用域实现混合策略。

如果您在Laravel项目中需要为模型统一注入业务级查询约束(如状态过滤、租户隔离、软删除增强等),但发现手动在每个查询中重复添加where条件导致冗余且易出错,则可通过全局作用域机制实现自动、可复用、可管理的查询逻辑注入。以下是实现Laravel框架全局查询设置的具体方法:
一、定义独立的作用域类
该方式适用于逻辑复杂、需跨多个模型复用或要求可测试性与可维护性的场景,符合SOLID设计原则,作用域行为明确且易于隔离调试。
1、在终端执行Artisan命令生成作用域类:php artisan make:scope TenantScope。
2、打开生成的app/Scopes/TenantScope.php文件,实现Illuminate\Database\Eloquent\Scope接口,并在apply方法中注入查询约束。
3、在apply方法内调用$builder->where('tenant_id', current_tenant_id()),确保所有查询自动绑定当前租户上下文。
4、在目标模型(如User)的booted静态方法中注册该作用域:static::addGlobalScope(new \App\Scopes\TenantScope);。
二、使用闭包定义全局作用域
该方式适用于轻量、一次性、仅作用于单个模型的查询约束,无需额外文件,开发效率高,适合快速验证或原型阶段。
1、在模型的booted方法中调用addGlobalScope,传入带唯一键名的闭包:static::addGlobalScope('active', function (Builder $builder) { $builder->where('status', 1); });。
2、闭包接收Builder $builder和Model $model两个参数,用于安全访问查询构造器与模型元信息。
3、确保该注册语句位于booted方法起始位置,以保障在首次查询前完成加载。
4、若需在特定查询中排除该闭包作用域,必须使用其注册时指定的字符串键名:User::withoutGlobalScope('active')->get();。
三、注册多个全局作用域并控制优先级
当一个模型需同时应用多种业务约束(如租户隔离 + 状态过滤 + 时间范围限制)时,可通过多次调用addGlobalScope叠加,其执行顺序与注册顺序一致,可用于构建分层过滤链。
1、在booted方法中按依赖关系依次注册多个作用域类:static::addGlobalScope(new TenantScope); static::addGlobalScope(new StatusScope); static::addGlobalScope(new TimeRangeScope);。
2、每个作用域类的apply方法将按注册顺序被依次调用,后注册的作用域可基于前序作用域已构建的查询条件进一步修饰。
3、若某作用域需覆盖前序作用域的字段(如重写select),应在apply中显式调用$builder->select(...)而非addSelect。
4、避免在闭包作用域中直接修改$builder->getQuery()->columns等底层属性,以防破坏Eloquent内部状态一致性。
四、临时禁用指定全局作用域
在后台管理、数据导出、迁移脚本等特殊场景下,需绕过默认全局约束以获取原始数据全集,此时必须精准移除目标作用域,防止影响其他约束逻辑。
1、对单一作用域类禁用:User::withoutGlobalScope(TenantScope::class)->get();。
2、对多个作用域类批量禁用:User::withoutGlobalScopes([TenantScope::class, StatusScope::class])->get();。
3、若需完全跳过所有全局作用域(含Laravel内置的SoftDeletingScope),使用withoutGlobalScopes()方法,但须谨慎评估副作用。
4、禁用操作仅对当次查询生效,不影响后续查询或其他模型实例的行为。
五、结合本地作用域实现混合查询策略
当部分业务逻辑需显式触发、而另一部分需默认强制生效时,可将全局作用域与本地作用域协同使用,兼顾隐式一致性与显式灵活性。
1、保留TenantScope作为全局作用域,确保所有用户查询默认隔离租户数据。
2、在模型中定义本地作用域方法:public function scopeWithTrashed($query) { return $query->withTrashed(); }。
3、在需要包含软删除记录的特定场景中,显式调用:User::withTrashed()->get();,该调用不受全局作用域中where('deleted_at', null)干扰。
4、本地作用域方法名必须以scope开头,且参数必须为$query,返回值必须为查询构造器实例以支持链式调用。


















