很多情况下,我们在数据查找时,有一部分条件会被重复且大量使用,通过作用域,将常用的SQL封装,会简化操作。
7.1 本地作用域
1、在某个条件下,只是这个模型对应的数据表使用,别的表并不使用,那么可以使用 本地作用域
将常用的SQL封装起来。
2、比如,在用户模块中,需要大量查询 状态为1
的数据,然后在且其他条件:
$users = User::where('status', 1)
->where('price', '>', 90)
->get();
这时,我们就可以将 状态为1
的这个片段,封装成一个单独的方法:
class User extends Model
{
/**
* App\Models\Models\User
*
* 查找 状态为1 的条件
*
* 语法:scope 开头,后面名称尽可能包含语义
*
* @param $query
* @return mixed
*/
public function scopeStatusTrue($query)
{
return $query->where('status', 1);
}
}
然后在这个模型下调用:
$users = User::statusTrue()
->where('price', '>', 90)
->get();
return [$users];
3、上面这种方式是比较死板的。可以通过参数传递的方式,使本地作用域更灵活:
/**
* App\Models\Models\User
*
* 查找 状态为value1,性别为value2 的条件
*
* 语法:scope 开头,后面名称尽可能包含语义
*
* @param $query
* @param int $value1 状态
* @param string $value2 性别
* @return mixed
*/
public function scopeStatusTrue($query, $value1 = 1, $value2 = '男')
{
return $query->where('status', $value1)->where('gender', $value2);
}
然后再使用:
$users = User::statusTrue(1, '女')
->where('price', '>', 90)
->get();
return [$users];
7.2 全局作用域
全局作用域,顾名思义就是在任意地方都可以有效的封装条件;
1、比如有个需求,不管在哪里操作,总是显示status 为1 的用户;那么首先在 app
目录下创建一个用于全局作用域的目录:Scopes
,然后创建一个类:StatusScope
,并让其实现Scope implements Scope
,如下:
这里,会报错,按 Alt+Enter
,根据提示添加代码即可。此时,完整的代码是这样的:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class StatusScope implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
// TODO: Implement apply() method.
}
}
然后在上面的 apply
方法中写入内容:
public function apply(Builder $builder, Model $model)
{
// TODO: Implement apply() method.
return $builder->where('status', 1);
}
此时,还不能实现全局,因为需要在模型设置个开关,让其富有灵活性;
在 User
模型中,写入如下内容:
use App\Scopes\StatusScope;//这里会自动引入
class User extends Model
{
//输入:booted,根据提示添加即可
protected static function booted()
{
parent::booted(); // TODO: Change the autogenerated stub
//添加这行代码
static::addGlobalScope(new StatusScope());
}
}
然后,在控制层,不需要添加任何设置,即可自动为所有查询添加 status=1
的条件。
2、如果某个全局,只是针对某个模块,并不需要创建一个全局类,直接闭包即可实现:
use Illuminate\Database\Eloquent\Builder;//注意,引入的是这个Builder
class User extends Model
{
//输入:booted,根据提示添加即可
protected static function booted()
{
///parent::booted(); // TODO: Change the autogenerated stub
///static::addGlobalScope(new StatusScope());
//使用闭包实现,第一个参数是状态名,必须起名
static::addGlobalScope('status', function (Builder $builder) {
return $builder->where('status', 1);
});
//可以再创建一个:
static::addGlobalScope('gender', function (Builder $builder) {
return $builder->where('gender', '男');
});
}
}
3、如果某个查询,不需要这个全局条件,可以单独移出掉:
//取消名称为status 的全局
$users = User::withoutGlobalScope('status')->get();
///取消名为status,gender的两个全局:
$users = User::withoutGlobalScopes(['status', 'gender'])->toSql();
//取消所有的全局类的条件
$users = User::withoutGlobalScope(StatusScope::class)->get();
以上。