【Laravel】为Eloquent 模型设置全局做用域和局部做用域进行查询

全局做用域

所谓「全局做用域」,指的是预置过滤器在注册该「全局做用域」的模型类的全部查询中生效,不须要指定任何额外条件。php

以 User 模型类为例,咱们在系统中可能只想针对已经验证过邮箱的用户进行操做,在没有介绍「做用域」以前,可能你会在应用中处处编写这样的代码:app

$users = User::whereNotNull('email_verified_at')->...

经过全局做用域类实现

要实现「全局做用域」,首先须要编写一个实现 Illuminate\Database\Eloquent\Scope 接口的全局做用域类,这里咱们将其命名为 EmailVerifiedAtScope,并将其放到 app/Scopes 目录下:函数

<?php
namespace App\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class EmailVerifiedAtScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        return $builder->whereNotNull('email_verified_at');
    }
}

在这个全局做用域类中,只须要实现 apply 方法便可,在该方法中,在查询构建器上应用过滤器方法并将其返回。post

而后,咱们须要将这个全局做用域类注册到 User 模型类上,这样,在 User 模型类上进行查询的时候才能够应用相应的过滤条件。这个工做能够经过在 User 模型类中重写父类的 boot 方法来完成:ui

protected static function boot()
{
    parent::boot();

    static::addGlobalScope(new EmailVerifiedAtScope());
}

注:boot 方法会在模型类实例化的时候调用。你能够在这里进行一些模型类的初始化操做。spa

经过匿名函数实现

若是你以为编写一个「全局做用域」类很麻烦,过滤逻辑又很简单,还能够在模型类的 boot 方法中经过匿名函数实现全局做用域:code

protected static function boot()
{
    parent::boot();

    //static::addGlobalScope(new EmailVerifiedAtScope());
    static::addGlobalScope('email_verified_at_scope', function (Builder $builder) {
        return $builder->whereNotNull('email_verified_at');
    });
}

实现效果和上面经过全局做用域类彻底同样。blog

移除全局做用域

在某些特定场景下,咱们可能须要移全局做用域,好比在后台用户管理页,咱们须要将未验证邮箱的用户页显示出来,这个时候咱们能够借助模型类的 withoutGlobalScope 方法来实现,该方法支持多种传参格式,移除多种全局做用域及其组合:接口

User::withoutGlobalScope(EmailVerifiedAtScope::class)->get(); # 指定类
User::withoutGlobalScope('email_verified_at_scope')->get();   # 匿名函数
User::withoutGlobalScopes()->get();  # 移除全部全局做用域
User::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();   # 移除多个类/匿名函数

局部做用域

所谓「局部做用域」,指的是预置过滤器在对应模型类的指定查询中生效,与「全局做用域」不一样,「局部做用域」须要额外指定才能生效,可是相应的,也更加灵活,能够适用于不一样场景。作用域

「局部做用域」的实现也比较简单,在须要应用它的模型类中定义一个过滤器方法便可。该方法须要以 scope 开头,而后附加该过滤器的名称,以文章列表页显示最流行文章为例(按照浏览数逆序),能够在 Post 模型类中编写一个 scopePopular 方法:

public function scopePopular(Builder $query)
{
    return $query->where('views', '>', '0')->orderBy('views', 'desc');
}

而在文章详情页,咱们但愿展现的是已发布的文章详情,若是文章没有发布,返回 404,所以咱们再定义一个「局部做用域」方法 scopeActive

public function scopeActive(Builder $query)
{
    return $query->where('status', Post::ACTIVED);
}

在模型类上调用「局部做用域」过滤器方法只需调用 scope 以后的过滤器名称便可,Eloquent 底层会经过魔术方法自动调用对应完整方法:

$post = Post::active()->find(100);
$post = Post::active()->popular()->get();

动态做用域

此外,Eloquent 模型类还支持「动态做用域」,所谓动态做用域指的是在查询过程当中动态设置预置过滤器的查询条件,动态做用域和局部做用域相似,过滤器方法名一样以 scope 开头,只不过能够经过额外参数指定查询条件,好比我要在文章中查询指定类型的文章,能够经过在 Post 模型类中定义以下方法:

public function scopeOfType(Builder $query, $type)
{
    return $query->where('type', $type);
}

这样,在查询指定类型的文章时,就能够这么实现:

$posts = Post::active()->ofType(Post::Article)->get();
相关文章
相关标签/搜索