一些查询需要在多个地方调用如何解决?用一下ORM全局作用域和局部作用域
学习链接
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();