PHP 查询作用域怎么定义

wen PHP项目 1

本文目录导读:

PHP 查询作用域怎么定义

  1. 本地作用域(Local Scopes)
  2. 动态作用域(带参数)
  3. 全局作用域(Global Scopes)
  4. 完整的实际示例
  5. 使用示例
  6. 高级技巧
  7. 关键要点
  8. 注意事项

在 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}%");
            });
    }
}

关键要点

  1. 命名约定:作用域方法必须以 scope 开头,使用时省略 scope 前缀
  2. 返回值:必须返回查询构建器实例($query
  3. 可链式调用:作用域可以相互链接
  4. 参数传递:第一个参数是 $query,后续是业务参数
  5. 避免逻辑重复:统一在作用域中处理常见过滤条件

注意事项

// ✅ 正确方式
public function scopeActive($query) { return $query->where('status', 1); }
User::active()->get();
// ❌ 错误方式
public function scopeActive($query) { $query->where('status', 1); } // 忘记 return

这样的设计让代码更加清晰、可维护,避免在控制器中重复编写复杂的查询逻辑。

抱歉,评论功能暂时关闭!