一. 需要解决什么问题
最近Laravel 项目中遇到一个需求,我有一个客户表,每个员工都有自己的客户,但是自己只能看自己的客户。
项目中,有很多功能需要查询客户列表,客户详情,查询客户入口很多,这些查询出来的数据,无一例外都只能查自己的。
问题来了,每个查询点都去补充限制条件,处理起来非常繁琐,而且维护成本很高,容易出现问题,一旦出现条件不到位,就可能造成不可估量的损失。
类似以上这种需求,应该有很多相似的。大家自觉往上靠。
提出疑问,可有办法统一处理,只维护一处即可,所有查询都带上条件限制。
答案肯定是有,以下是其中一个解决思路,也是推荐的。
二. 什么是查询作用域
查询作用域(Query Scope)是 Laravel Eloquent ORM 提供的一个强大功能,它允许你封装常用的查询逻辑,使代码更简洁、可重用。合理使用可以大幅提高代码质量和开发效率。

三. 作用域怎么用
3.1 全局作用域
全局作用域可以为模型的所有查询添加约束。最常见的 软删除 功能就是利用全局范围仅从数据库中检索「未删除」模型。
3.1.1 开发全局作用域
编写作用域类,目录可以是任意目录,我创建在 App\Models\Scopes 下面
<?phpnamespace App\Models\Scopes;use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;class ClientScope implements Scope
{public function apply(Builder $builder, Model $model): void{$builder->where('uid_code', '=', get_admin_code());}
} 
代码解释:
- 需要实现 
Illuminate\Database\Eloquent\Scope接口 Scope接口要求实现apply方法。需要完善 apply 方法apply方法里面补充的就是所需要的限制条件,例如我的就是指定 uid_code 等于 当前登录用户code
在需要模型里面,配置全局作用域
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\Scopes\ClientScope;
use Illuminate\Database\Eloquent\Factories\HasFactory;class ClientModel extends Model
{use HasFactory;protected static function booted(): void{static::addGlobalScope(new ClientScope());}
}
 
代码解释:
- 重写模型的 
booted方法并使用addGlobalScope方法 addGlobalScope方法可以接受作用域的一个实例参数,也就是上面编写的作用域
完成上面的步骤即可,在使用 ClientModel 模型查询时,都会带上作用域里面的条件。
3.1.2 匿名全局作用域
是不是感觉上面的代码繁琐了,为了一个全局作用域还需要单独去编写一个类。那么使用闭包来定义全局作用域,可以简化流程。
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;class ClientModel extends Model
{use HasFactory;protected static function booted(): void{static::addGlobalScope('clientScope', function (Builder $builder) {$builder->where('uid_code', '=', get_admin_code());});}
}
 
代码解释:
- 重写模型的 
booted方法并使用addGlobalScope方法 addGlobalScope方法,第一个参数为作用域名称,第二个参数为匿名函数,里面补充就是相关条件。
注意:
匿名全局作用域适用于单独模型使用,如果有多个模型,都需要同样的限制条件,则还是需要创建作用域类的。方便统一维护。视情况而定
3.1.3 取消全局作用域
如果某个查询不需要这个全局限制,那么就可以取消全局作用域
- 取消部分,或者指定作用域
 
//取消全局作用域类
ClientModel::withoutGlobalScope(ClientScope::class)->get();
 
//取消闭包作用域
ClientModel::withoutGlobalScope('clientScope')->get();
 
// 取消部分作用域...
ClientModel::withoutGlobalScopes([ClientScope::class, SecondScope::class
])->get();
 
- 取消全部作用域
 
// 取消全部全局作用域...
ClientModel::withoutGlobalScopes()->get();
 
3.2 局部作用域
局部作用域允许你定义通用的查询约束,可以链式调用。
// 在模型中定义
class ClientModel extends Model
{public function scopePopular($query){return $query->where('votes', '>', 100);}public function scopeActive($query){return $query->where('active', 1);}
}
 
使用方法:
// 单个作用域
$users = ClientModel::popular()->get();// 链式调用多个作用域
$users = ClientModel::popular()->active()->orderBy('created_at')->get();//使用orWhere实现
$users = ClientModel::popular()->orWhere->active()->get();//使用闭包实现
$users = ClientModel::popular()->orWhere(function (Builder $query) {$query->active();
})->get();
 
3.3 动态作用域
动态作用域允许你传递参数给作用域:
// 定义
class ClientModel extends Model
{public function scopeOfType($query, $type){return $query->where('type', $type);}
} 
注意:作用域参数要放在 $query 参数之后
// 使用
$users = ClientModel::ofType('admin')->get();
 
四. 总结
查询作用域是 Laravel Eloquent 中组织查询逻辑的强大工具,合理使用可以大幅提高代码质量和开发效率。
合理使用 全局作用域和局部作用域,可以使查询逻辑更清晰易懂,方便维护,降低后期的查看查看成本。