Laravel 中 scope 作用域

一、查询作用域

1.1 全局作用域

全局作用域可以给模型的查询都添加上约束。
Laravel 的 软删除 功能就是利用此特性从数据库中获取 「未删除」的模型。
你可以编写你自己的全局作用域,很简单、方便的为每个模型查询都加上约束条件:

1 编写全局作用域

编写全局作用域很简单。
定义一个实现 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 AgeScope implements Scope
{
    
    
    /**
     * 把约束加到 Eloquent 查询构造中。
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
    
    
        $builder->where('age', '>', 200);
    }
}

{tip} 如果需要在 select 语句里添加字段,应使用 addSelect 方法,而不是 select 方法。
这将有效防止无意中替换现有 select 语句的情况。

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

<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    
    
    /**
     *  模型的 「启动」 方法.
     *  @return void
     */
    protected static function boot()
    {
    
    
        parent::boot();
        static::addGlobalScope(new AgeScope);
    }
}

添加作用域后,对 User::all() 的查询会生成以下 SQL 查询语句:

select * from `users` where `age` > 200

2 匿名全局作用域

Eloquent 同样允许使用闭包定义全局作用域,这样就不需要为一个简单的作用域而编写一个单独的类:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class User extends Model
{
    
    
    /**
     *模型的「启动」方法.
     * @return void
     */
    protected static function boot()
    {
    
    
        parent::boot();

        static::addGlobalScope('age', function (Builder $builder) {
    
    
            $builder->where('age', '>', 200);
        });
    }
}

3 取消全局作用域

如果需要对当前查询取消全局作用域,需要使用 withoutGlobalScope 方法。
该方法仅接受全局作用域类名作为它唯一的参数:

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

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

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

如果你需要取消部分或者全部的全局作用域的话,需要使用 withoutGlobalScopes 方法:

// 取消所有的全局作用域...
User::withoutGlobalScopes()->get();

// 取消部分全局作用域...
User::withoutGlobalScopes([
    FirstScope::class, SecondScope::class
])->get();

1.2 本地作用域

本地作用域允许定义通用的约束集合以便在应用程序中重复使用。
例如,你可能经常需要获取所有 「流行」的用户。

1 编写本地作用域

要定义这样一个范围,只需要在对应的 Eloquent 模型方法前添加 scope 前缀:

作用域总是返回一个查询构造器实例:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    
    
    /**
     * 只查询受欢迎的用户的作用域.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
    
    
        return $query->where('votes', '>', 100);
    }

    /**
     * 只查询 active 用户的作用域.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @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();

因为这样可能会有点麻烦,Laravel 提供了「高阶的」 orWhere 方法,它允许你在链式调用作用域时不使用闭包:

$users = App\User::popular()->orWhere->active()->get();

2 动态作用域

有时可能地希望定义一个可以接受参数的作用域。
把额外参数传递给作用域就可以达到此目的。
作用域参数要放在 $query 参数之后:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    
    
    /**
     * 将查询作用域限制为仅包含给定类型的用户。
     *
     * @param  \Illuminate\Database\Eloquent\Builder $query
     * @param  mixed $type
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, $type)
    {
    
    
        return $query->where('type', $type);
    }
}

这样就可以在调用作用域时传递参数了:

$users = App\User::ofType('admin')->get();

二、应用示例

$user  = new ChatUser();
$chat_user = $user::scopeNickName($user,"大金")->get();
dd($chat_user->toArray());

运行结果

rray:1 [
  0 => array:35 [
    "id" => 87
    "mpid" => 1
    "openid" => "oYxpK0RsvcwgS9DtmIOuyb_BgJbo"
    "nickname" => "大金"
    "headimgurl" => "https://img-home.csdnimg.cn/images/20210817021749.png"
    "sex" => 1
    "subscribe" => 1
    "subscribe_time" => 1508920878
    "unsubscribe_time" => null
    "relname" => null
    "signature" => null
    "mobile" => null
    "is_bind" => 0
    "language" => "zh_CN"
    "country" => "中国"
    "province" => "河南"
    "city" => "商丘"
    "remark" => ""
    "group_id" => 0
    "groupid" => 0
    "tagid_list" => "[]"
    "score" => 0
    "money" => "0.00"
    "latitude" => null
    "longitude" => null
    "location_precision" => null
    "type" => 0
    "unionid" => null
    "password" => null
    "last_time" => 586969200
    "parentid" => 1
    "isfenxiao" => 0
    "totle_earn" => "0.00"
    "balance" => "0.00"
    "fenxiao_leavel" => 2
  ]
]

猜你喜欢

转载自blog.csdn.net/weiguang102/article/details/125088482