作为程序员一定要保持良好的睡眠,才能好编程

laravel查询作用域 全局作用域、局部作用域

发布时间:2019-05-26

全局作用域允许我们为给定模型的所有查询添加条件约束。Laravel 自带的软删除功能就使用了全局作用域来从数据库中拉出所有没有被删除的模型。编写自定义的全局作用域可以提供一种方便的、简单的方式来确保给定模型的每个查询都有特定的条件约束。


项目中我们是实现了自己的软删除,凡是这里的应用都需要加上 status > 0 这样的一句条件,


如果每一条语句都加的话,会非常麻烦,laravel提供了查询作用域,下面看下如何使用吧。


全局作用域

编写全局作用域

Laravel 应用默认并没有为作用域预定义文件夹,所以你可以按照自己的喜好在 app 目录下创建 Scopes 目录。


自己创建目录


自定义全局作用域很简单,首先定义一个实现 Illuminate\Database\Eloquent\Scope 接口的类,该接口要求你实现一个方法:apply。需要的话可以在 apply 方法中添加 where 条件到查询:


<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class StatusScope implements Scope
{
    /**
     * 应用作用域到给定的Eloquent查询构建器.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     * @translator laravelacademy.org
     */
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('status', '>', 0);
    }
}



注:如果你的全局作用域需要添加列到查询的 select 子句,需要使用 addSelect 方法来替代 select,这样就可以避免已存在的 select 查询子句造成影响。



应用全局作用域

要将全局作用域应用到模型,需要重写给定模型的 boot 方法并使用 addGlobalScope 方法:

在我的项目中,可以写在app\core\Models.php中


<?php
namespace App;
use App\Scopes\StatusScope;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    /**
     * 模型的“启动”方法.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();
        static::addGlobalScope(new StatusScope);
    }
}



添加作用域后,如果使用 User::all() 查询则会生成如下 SQL 语句:

status>0 就是应用的效果

select * from `users` where `status` > 0




匿名的全局作用域

Eloquent 还允许我们使用闭包定义全局作用域,这在实现简单作用域的时候特别有用,


这样的话,我们就没必要定义一个单独的类了:


我们的项目,使用这种方式会更好:


<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class User extends Model{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();
        static::addGlobalScope('age', function(Builder $builder) {
            $builder->where('age', '>', 200);
        });
    }
}


移除全局作用域

定义了全局作用域,可以不使用,通过 withoutGlobalScope() 方法跳过


如果想要在给定查询中移除指定全局作用域,可以使用 withoutGlobalScope 方法,该方法接收全局作用域的类名作为其唯一参数:

User::withoutGlobalScope(AgeScope::class)->get();

或者,如果你使用闭包定义的全局作用域的话:


User::withoutGlobalScope('age')->get();

如果你想要移除某几个或全部全局作用域,可以使用 withoutGlobalScopes 方法:


// 移除所有全局作用域

User::withoutGlobalScopes()->get();

 //移除某些全局作用域   

 User::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();




本地作用域

本地作用域允许我们定义通用的约束集合以便在应用中复用。例如,你可能经常需要获取最受欢迎的用户,要定义这样的一个作用域,

只需简单在对应 Eloquent 模型方法前加上一个 scope 前缀。


作用域总是返回查询构建器实例:


<?php


namespace App;


use Illuminate\Database\Eloquent\Model;


class User extends Model

{

    /**

     * 只包含活跃用户的查询作用域

     *

     * @return \Illuminate\Database\Eloquent\Builder

     */

    public function scopePopular($query)

    {

        return $query->where('votes', '>', 100);

    }


    /**

     * 只包含激活用户的查询作用域

     *

     * @return \Illuminate\Database\Eloquent\Builder

     */

    public function scopeActive($query)

    {

        return $query->where('active', 1);

    }

}


使用本地作用域


作用域被定义好了之后,就可以在查询模型的时候调用作用域方法,但调用时不需要加上 scope 前缀,你甚至可以同时调用多个作用域,例如:

$users = App\User::popular()->active()->orderBy('created_at')->get();

可以使用闭包回调通过 or 查询操作符联合多个 Eloquent 模型作用域:


$users = App\User::popular()->orWhere(function (Builder $query) {
    $query->active();
})->get();


这里就不能是使用all() 这个方法了。




$data=User::active()->get();

return $data;

通过这样的形式调用。