【Laravel】爲Eloquent 模型設置全局做用域和局部做用域進行查詢

全局做用域

所謂「全局做用域」,指的是預置過濾器在註冊該「全局做用域」的模型類的全部查詢中生效,不須要指定任何額外條件。php

以 User 模型類爲例,咱們在系統中可能只想針對已經驗證過郵箱的用戶進行操做,在沒有介紹「做用域」以前,可能你會在應用中處處編寫這樣的代碼:app

$users = User::whereNotNull('email_verified_at')->...

經過全局做用域類實現

要實現「全局做用域」,首先須要編寫一個實現 Illuminate\Database\Eloquent\Scope 接口的全局做用域類,這裏咱們將其命名爲 EmailVerifiedAtScope,並將其放到 app/Scopes 目錄下:函數

<?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');
    }
}

在這個全局做用域類中,只須要實現 apply 方法便可,在該方法中,在查詢構建器上應用過濾器方法並將其返回。post

而後,咱們須要將這個全局做用域類註冊到 User 模型類上,這樣,在 User 模型類上進行查詢的時候才能夠應用相應的過濾條件。這個工做能夠經過在 User 模型類中重寫父類的 boot 方法來完成:ui

protected static function boot()
{
    parent::boot();

    static::addGlobalScope(new EmailVerifiedAtScope());
}

注:boot 方法會在模型類實例化的時候調用。你能夠在這裏進行一些模型類的初始化操做。spa

經過匿名函數實現

若是你以爲編寫一個「全局做用域」類很麻煩,過濾邏輯又很簡單,還能夠在模型類的 boot 方法中經過匿名函數實現全局做用域:code

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');
    });
}

實現效果和上面經過全局做用域類徹底同樣。blog

移除全局做用域

在某些特定場景下,咱們可能須要移全局做用域,好比在後臺用戶管理頁,咱們須要將未驗證郵箱的用戶頁顯示出來,這個時候咱們能夠藉助模型類的 withoutGlobalScope 方法來實現,該方法支持多種傳參格式,移除多種全局做用域及其組合:接口

User::withoutGlobalScope(EmailVerifiedAtScope::class)->get(); # 指定類
User::withoutGlobalScope('email_verified_at_scope')->get();   # 匿名函數
User::withoutGlobalScopes()->get();  # 移除全部全局做用域
User::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();   # 移除多個類/匿名函數

局部做用域

所謂「局部做用域」,指的是預置過濾器在對應模型類的指定查詢中生效,與「全局做用域」不一樣,「局部做用域」須要額外指定才能生效,可是相應的,也更加靈活,能夠適用於不一樣場景。作用域

「局部做用域」的實現也比較簡單,在須要應用它的模型類中定義一個過濾器方法便可。該方法須要以 scope 開頭,而後附加該過濾器的名稱,以文章列表頁顯示最流行文章爲例(按照瀏覽數逆序),能夠在 Post 模型類中編寫一個 scopePopular 方法:

public function scopePopular(Builder $query)
{
    return $query->where('views', '>', '0')->orderBy('views', 'desc');
}

而在文章詳情頁,咱們但願展現的是已發佈的文章詳情,若是文章沒有發佈,返回 404,所以咱們再定義一個「局部做用域」方法 scopeActive

public function scopeActive(Builder $query)
{
    return $query->where('status', Post::ACTIVED);
}

在模型類上調用「局部做用域」過濾器方法只需調用 scope 以後的過濾器名稱便可,Eloquent 底層會經過魔術方法自動調用對應完整方法:

$post = Post::active()->find(100);
$post = Post::active()->popular()->get();

動態做用域

此外,Eloquent 模型類還支持「動態做用域」,所謂動態做用域指的是在查詢過程當中動態設置預置過濾器的查詢條件,動態做用域和局部做用域相似,過濾器方法名一樣以 scope 開頭,只不過能夠經過額外參數指定查詢條件,好比我要在文章中查詢指定類型的文章,能夠經過在 Post 模型類中定義以下方法:

public function scopeOfType(Builder $query, $type)
{
    return $query->where('type', $type);
}

這樣,在查詢指定類型的文章時,就能夠這麼實現:

$posts = Post::active()->ofType(Post::Article)->get();
相關文章
相關標籤/搜索