很多情况下,我们在数据查找时,有一部分条件会被重复且大量使用,通过作用域,将常用的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 ,根据提示添加代码即可。此时,完整的代码是这样的:
<?phpnamespace 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;//注意,引入的是这个Builderclass 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();
以上。
