Laravel 中的查询作用域详解
在 Laravel 的 Eloquent ORM 中,查询作用域(Query Scopes)是强大的工具,允许你封装常用的查询逻辑,使代码更简洁、可复用且易于维护。
#Laravel
#查询作用域
#PHP
#后端开发
Laravel 中的查询作用域详解
在 Laravel 的 Eloquent ORM 中,查询作用域(Query Scopes)是强大的工具,允许你封装常用的查询逻辑,为指定模型的所有查询添加约束,使代码更简洁、可复用且易于维护。Laravel 自身的软删除功能正是利用全局作用域,确保仅从数据库检索”未删除”的模型。
查询作用域分为两种类型:局部作用域和全局作用域。
局部作用域 (Local Scopes)
局部作用域是最常用的类型,它们允许你定义可链式调用的查询约束。
基本特征
- 命名约定:方法名以 scope 开头(如 scopeActive)
- 调用方式:调用时去掉 scope 前缀,使用驼峰命名(如 active())
- 参数:可以接受额外参数
- 返回值:必须返回查询构建器实例
定义局部作用域
// 在模型中定义
public function scopeActive($query)
{
return $query->where('status', 'active');
}
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
使用局部作用域
// 基本使用
$users = User::active()->get();
// 带参数
$admins = User::ofType('admin')->get();
// 链式调用
$users = User::active()->ofType('premium')->orderBy('name')->get();
动态作用域
你可以创建更灵活的动态作用域:
public function scopeWhereLike($query, $column, $value)
{
return $query->where($column, 'like', '%'.$value.'%');
}
// 使用
User::whereLike('email', '@gmail.com')->get();
全局作用域 (Global Scopes)
全局作用域会自动应用到模型的所有查询上,非常适合添加全局约束(如多租户隔离、软删除等)。
生成全局作用域
从 Laravel 11 开始,我们可以调用 make:scope
Artisan 命令生成的查询作用域,生成的文件保存在应用程序的 app/Models/Scopes 目录中:
php artisan make:scope AncientScope
Laravel 11 之前的版本须手动创建查询作用域,且未对文件保存位置做出要求。
定义全局作用域
方法 1:使用闭包
// 在模型的 boot 方法中注册
protected static function booted()
{
static::addGlobalScope('active', function (Builder $builder) {
$builder->where('active', true);
});
}
方法 2:使用 Scope 类
- 创建 Scope 类:
// app/Scopes/TenantScope.php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class TenantScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('tenant_id', auth()->user()->tenant_id);
}
}
- 在模型中注册:
protected static function booted()
{
static::addGlobalScope(new TenantScope);
}
移除全局作用域
// 移除特定作用域
User::withoutGlobalScope(TenantScope::class)->get();
// 移除闭包作用域
User::withoutGlobalScope('active')->get();
// 移除所有全局作用域
User::withoutGlobalScopes()->get();
// 移除多个作用域
User::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();
查询作用域的最佳实践
1. 保持作用域单一职责
// 好:单一职责
public function scopeActive($query) {
return $query->where('active', true);
}
public function scopeRecent($query) {
return $query->orderBy('created_at', 'desc');
}
// 避免:职责过多
public function scopeActiveRecentUsers($query) {
return $query->where('active', true)->orderBy('created_at', 'desc');
}
2. 组合使用作用域
// 控制器中
public function index()
{
return User::active()
->verified()
->withRole('admin')
->orderByName()
->paginate(10);
}
3. 在作用域中使用条件逻辑
public function scopeFilter($query, array $filters)
{
return $query->when($filters['search'] ?? null, function ($query, $search) {
$query->where(function ($query) use ($search) {
$query->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
})->when($filters['role'] ?? null, function ($query, $role) {
$query->whereRole($role);
});
}
4. 作用域中的关系处理
public function scopeWithPostsCount($query)
{
return $query->withCount(['posts' => function ($query) {
$query->where('published', true);
}]);
}
// 使用
$users = User::withPostsCount()->get();
查询作用域 vs 普通方法
特性 | 查询作用域 | 普通方法 |
---|---|---|
调用方式 | Model::scopeName() | $model->method() |
返回值 | 查询构建器实例 | 任意类型 |
链式调用 | 支持 | 不一定 |
应用位置 | 查询过程中 | 模型实例上 |
典型用途 | 构建查询约束 | 业务逻辑处理 |
常见应用场景
1. 状态过滤:
public function scopePending($query) {
return $query->where('status', 'pending');
}
2. 时间范围:
public function scopeCreatedBetween($query, $from, $to) {
return $query->whereBetween('created_at', [$from, $to]);
}
3. 排序逻辑:
public function scopePopular($query) {
return $query->orderBy('views', 'desc');
}
4. 软删除处理:
public function scopeWithTrashed($query) {
return $query->withTrashed();
}
5. 权限限制:
public function scopeVisibleTo($query, User $user) {
return $query->where('organization_id', $user->organization_id);
}
总结
Laravel 的查询作用域是优化数据库查询的强大工具:
- 局部作用域:用于创建可复用的查询片段,通过链式调用组合复杂查询
- 全局作用域:用于自动应用全局查询约束
- 提高代码质量:减少重复代码,提高可读性和可维护性
- 灵活的参数支持:可以接受各种参数创建动态查询
- 组合使用:多个作用域可以链式组合构建复杂查询
合理使用查询作用域可以显著提升 Laravel 应用的代码质量和开发效率,是 Eloquent ORM 的核心特性之一。