一些查询需要在多个地方调用如何解决?用一下ORM全局作用域和局部作用域

未匹配的标注

学习链接

xueyuanjun.com/post/9710

Eloquent 模型类提供的「Scope」功能就可以帮我们实现这种优化。「Scope」字面意义上翻译为「作用域」,有点不那么好理解,从功能上来说,把它看作预置的「过滤器」更合适。我们将那些需要在多处调用的查询条件编写过滤器,然后将调用查询代码的地方改为调用过滤器,调用过滤器比编写那些冗长而重复的查询方法更加便捷,可读性也更好。

从调用方式或者过滤器的作用范围来说,可以把「作用域」分为「全局作用域」和「局部作用域」。「作用域」都是围绕模型类展开的,不管是全局作用域还是局部作用域,都是作用到某个模型类上。

全局作用域、

是什么?

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

怎么用?

1. 编写 全局作用域类,实现apply 方法

app/Scopes 目录下:创建 全局作用域类: EmailVerifiedAtScope

类中需要 实现Illuminate\Database\Eloquent\Scope 接口

在这个全局作用域类中,只需要实现其中的 apply 方法即可。

<?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');
    }
}

2. 注册这个全局作用域类到 指定模型类(重写父类boot方法)

模型类中重写父类的 boot 方法来完成

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

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

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

如何调用?

这样,当我们通过 该 模型类进行查询的时候,就会自动应用全局作用域指定的查询条件了

提问:我看你是调用静态类方法,那我可以写闭包吗?

可以。如下:

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');
    });
}

我个别查询不需要全局作用域?移除全局作用域?

如下:withoutGlobalScope

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

局部作用域

是什么?

更加灵活的作用域,相对的它的使用,是需要手动指定的

如何用?

1. 创建局部作用域,定义模型的过滤器方法

在需要应用它的模型类中定义一个过滤器方法即可。

注意:该方法需要以 scope 开头,然后附加该过滤器的名称。
以文章列表页显示最流行文章为例(按照浏览数逆序),可以在 Post 模型类中编写一个 scopePopular 方法:

public function scopePopular(Builder $query)
{
    return $query->where('views', '>', '0')->orderBy('views', 'desc');
}
// 再来一个
public function scopeActive(Builder $query)
{
    return $query->where('status', Post::ACTIVED);
}

如何调用?

在模型类上调用「局部作用域」过滤器方法只需调用 scope 之后的过滤器名称即可,Eloquent 底层会通过魔术方法自动调用对应完整方法。

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

是否可以传递参数给作用域?动态作用域

与局部作用域相似,只不过多了参数。
也是 scope 开头。

定义

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

调用

$posts = Post::active()->ofType(Post::Article)->get();

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~