3分鐘短文:Laravel模型作用域,為你“節省”更多程式碼

ragus發表於2020-10-04

引言

原則上程式碼寫一次,處處是引用,不需要大量的冗餘程式碼,這是一種趨勢,也是提高程式碼健壯性的努力方向。

laravel模型為我們提供了一層資料庫操作層,將資料互動獨立出來。

但是久而久之,隨著專案的需求不斷擴大,最常用的查詢操作,同樣會有大量的冗餘程式碼。

img

本文就來講講,連模型的自我瘦身,縮減模型的程式碼。

全域性作用域

假設有些資料庫查詢操作,無論是在控制器內,或者在模板檔案內,或者命令列方法內,都有重複的使用需求,要是在模型內有一個公用的方法,預設就加上這些篩選條件,就可以顯著減少程式碼量了。

比如有一個查詢條件:

$publishedEvents = Event::where('published', '=', 1)->get();

上述程式碼最後生成的SQL語句如下:

SELECT * FROM events WHERE `published` = 1;

如果條件 published = 1 在預設的情況下需要開啟,我們可以使用laravel模型的 全域性作用域 方式為所有查詢追加上這個條件。

在模型檔案 Event 內頭部引入下述類:

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

在模型類內部,手動實現 boot 方法:

protected static function boot()
{
    parent::boot();
    static::addGlobalScope('published', function (Builder $builder) {
        $builder->where('published', '=', 1);
    });
}

這樣SQL語句 where published = 1 就會追加到所有的模型查詢方法內,只要是建立生成了 QueryBuilder 物件的,都會附加上此約束語句。

那有些讀者可能要問了:“如果我不想要這個約束語句,豈不是連模型也永不了了?

那哪兒能呢!不過就是QueryBuilder的一個屬性陣列的一個元素而已,手動移除就行了,這樣特例問題就解決了。

$events = Event::withoutGlobalScopes()->get();

看到了吧,追加上很簡單,移除更簡單。

本地作用域

接上一節的 withoutGlobalScope 要每次手動遮蔽的方式不同,有時候使用有侷限的作用域更能解決問題。所以,本地作用域 應運而生,專門用於某個模型檔案的方法,手動呼叫的時候就起作用,不呼叫就不會主動追加。

而宣告一個本地作用域,只要遵循laravel的語法規定即可,如下示例:

public function scopePublished($query)
{
    return $query->where('published', 1);
}

只需要宣告一個以 scope 為首的小駝峰命名的函式方法即可,並返回一個 QueryBuilder 物件例項。呼叫的時候要手動追加上:

$events = Event::published()->get();

其中 published()方法就是對映到 scopePublished 方法。

上面的演示程式碼,沒有接收使用者輸入,下面演示一下帶參的傳遞方式。比如有這樣一個查詢需求:

$events = Event::where('zip', $zipCode)->get();

使用本地作用域實現出來:

public function scopeZip($query, $zip)
{
    return $query->where('zip', $zip);
}

按照位置傳入即可。使用的使用,直接傳入:

$zip = '43016';
$events = Event::zip($zip)->get();

這樣就完成了本地作用域的使用,是不是很直觀。

既然本地作用域返回的是 QueryBuilder 例項,那麼自然就可以鏈式呼叫本地作用域的方法,和 QueryBuilder 的方法。我們再宣告一個本地作用域方法:

public function scopeAttendees($query, $maximum)
{
    return $query->where('max_attendees', $maximum);
}

現在把上述兩個方法串聯使用:

$events = Event::zip(43016)->attendees(2)->get();

生成的SQL語句也符合預期:

SELECT * FROM events WHERE zip = '43016' and max_attendees = '2';

寫在最後

本期我們又舊事重提,把laravel模型的作用域設計方法拿出來溫習了一下。講述了兩個方法:

  • 全域性作用域:全域性起作用,需要手動移除;

  • 本地作用域:只有手動呼叫起作用,可鏈式使用;

這樣的設計模式可以很大程度上節約查詢程式碼,但是對於維護,需要同等熟悉的開發者彼此遵循開發規範,寫出可維護的程式碼。

Happy coding :-)

我是@程式設計師小助手,專注程式設計知識,圈子動態的IT領域原創作者

本作品採用《CC 協議》,轉載必須註明作者和本文連結
write-less-do-more-make-you-out-of-door

相關文章