本文目录导读:

在 PHP 中,查询作用域(Query Scope)是 Laravel Eloquent ORM 的核心特性之一,下面我来详细介绍如何定义和使用查询作用域:
本地作用域(Local Scopes)
基本定义方法
在模型中定义以 scope 开头的方法:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class User extends Model
{
/**
* 定义本地作用域
* 方法名:scope + 作用域名(驼峰式)
*/
public function scopeActive($query)
{
return $query->where('status', 'active');
}
public function scopeAdmin($query)
{
return $query->where('role', 'admin');
}
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
}
使用本地作用域
// 基本使用
$activeUsers = User::active()->get();
// 链式调用
$activeAdmins = User::active()->admin()->get();
// 带参数的作用域
$users = User::ofType('premium')->get();
// 组合使用
$recentActiveUsers = User::active()
->where('created_at', '>', now()->subDays(30))
->orderBy('created_at', 'desc')
->get();
动态作用域(带参数)
class Product extends Model
{
/**
* 带多个参数的作用域
*/
public function scopePriceBetween($query, $min, $max)
{
return $query->whereBetween('price', [$min, $max]);
}
public function scopeSearch($query, $term, $columns = ['name', 'description'])
{
return $query->where(function ($q) use ($term, $columns) {
foreach ($columns as $column) {
$q->orWhere($column, 'LIKE', "%{$term}%");
}
});
}
}
// 使用动态作用域
$products = Product::priceBetween(100, 500)->get();
$results = Product::search('keyword', ['name', 'sku'])->get();
全局作用域(Global Scopes)
全局作用域会在所有查询中自动应用:
使用匿名函数
class User extends Model
{
protected static function booted()
{
static::addGlobalScope('active', function (Builder $builder) {
$builder->where('status', 'active');
});
}
}
创建作用域类
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class ActiveScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('status', 'active');
}
}
在模型中使用全局作用域类
class User extends Model
{
protected static function booted()
{
static::addGlobalScope(new ActiveScope);
}
// 移除全局作用域的方法
public static function withoutGlobalScope($scope)
{
return static::withoutGlobalScope($scope);
}
}
完整的实际示例
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use App\Scopes\ActiveScope;
class Post extends Model
{
protected $table = 'posts';
// 添加全局作用域
protected static function booted()
{
static::addGlobalScope(new ActiveScope);
}
/**
* 查发布状态的文章
*/
public function scopePublished($query)
{
return $query->where('status', 'published');
}
/**
* 按作者筛选
*/
public function scopeByAuthor($query, $authorId)
{
return $query->where('author_id', $authorId);
}
/**
* 获取热门文章
*/
public function scopePopular($query, $minViews = 1000)
{
return $query->where('views', '>=', $minViews)
->orderBy('views', 'desc');
}
/**
* 带排序的作用域
*/
public function scopeSorted($query, $sortBy = 'created_at', $order = 'desc')
{
return $query->orderBy($sortBy, $order);
}
}
使用示例
// 控制器中使用
class PostController extends Controller
{
public function index(Request $request)
{
$posts = Post::query()
->published()
->popular(500)
->byAuthor($request->user()->id)
->paginate(10);
return view('posts.index', compact('posts'));
}
}
高级技巧
动态添加查询条件
class Product extends Model
{
public function scopeFilter($query, array $filters)
{
return $query
->when($filters['category'] ?? null, function ($query, $category) {
return $query->where('category', $category);
})
->when($filters['min_price'] ?? null, function ($query, $minPrice) {
return $query->where('price', '>=', $minPrice);
})
->when($filters['search'] ?? null, function ($query, $search) {
return $query->where('name', 'like', "%{$search}%");
});
}
}
关键要点
- 命名约定:作用域方法必须以
scope开头,使用时省略scope前缀 - 返回值:必须返回查询构建器实例(
$query) - 可链式调用:作用域可以相互链接
- 参数传递:第一个参数是
$query,后续是业务参数 - 避免逻辑重复:统一在作用域中处理常见过滤条件
注意事项
// ✅ 正确方式
public function scopeActive($query) { return $query->where('status', 1); }
User::active()->get();
// ❌ 错误方式
public function scopeActive($query) { $query->where('status', 1); } // 忘记 return
这样的设计让代码更加清晰、可维护,避免在控制器中重复编写复杂的查询逻辑。