iT邦幫忙

2023 iThome 鐵人賽

DAY 9
2

前一天我們說到了 Repository Pattern 的問題之後,現在回到了 Laravel 專案中,如果不用 Repository Pattern 的話,我們還有什麼選擇呢?

方法1: 使用 Elquent 的 Query Builder

下面範例使用到了自定義的 Query Builder

// Channel.php
use App\Models\Builder\ChannelBuilder;
use Illuminate\Database\Eloquent\Builder;

class Channel extends Base
{
    // ...
    public static function query(): ChannelBuilder|Builder
    {
        return parent::query();
    }

    public function newEloquentBuilder($query): ChannelBuilder
    {
        return new ChannelBuilder($query);
    }
    // ...
}

// ChannelBuilder.php
class ChannelBuilder extends Builder
{
    use Common;

    // ...
    public function isPublished(): self
    {
        return $this->whereStatus(ChannelStatus::published);
    }
}

// Common.php
trait Common
{
    public function whereStatus(int|BackedEnum $status): self
    {
        if ($status instanceof BackedEnum) {
            $status = $status->value;
        }

        return $this->where('status', $status);
    }
}

// ChannelStatus.php
enum ChannelStatus :int
{
    case pending = 0;
    case published = 1;
    case unpublished = 2;
}

在這個範例中,不僅可以讓查詢情境可以遵守 DRY 原則,而且可以讓你更好去拆解 Model 的常用查詢。在開發過程中,也可以直接讓 IDE 有辦法透過靜態分析找到對應的查詢方法。

Note: 基於上面的作法,所以我不建議使用 laravel 內建的 scope

方法2: 使用自訂的查詢組件

class LatestEpisodeQuery
{
    public function __constructor(
        private readonly Channel $channel,
        private readonly ?int    $limit = null,
    ): void
    {
        $this->limit ??= config('episodes.limit');
    }

    public function get(): Collection
    {
        return $this
            ->channel
            ->episodes()
            ->isPublished()
            ->isPremium()
            ->latest()
            ->take($this->limit)
            ->get();
    }
}


// usage
(new LatestEpisodeQuery($channel))->get();

在這個範例裡面,還可以將共用的查詢抽出成 query() ,然後去組合你想要的複雜查詢。

方法3: 使用 Actions 去組織你的情境

一個 Action class 可以包含像是簡單的 CRUD,複雜可以到不同的 model 間的資料處理,你可以想成只有 CRUD 的 repository 加上有一定程度複雜的商業邏輯的組合。

Action 基本上有著高度可以重用的性質與彈性組合的特性,但是實作上其實沒有一個主流的 guideline 可以參考,但是可以嘗試建立類似下面的規則

  • 使用 Composite 模式,不要繼承某個 Action 或是被別的 Action 所繼承
  • Action 內部必須要使用 Dependency Injection 來解除依賴
  • Action 內只能有一個 public method , 可以叫做是 execute() 或是 run(),其他在 action 中的方法必須都要是 private method

範例可以參考此文中,所列出來的範例。

雖然在這邊我列入為 方法 3 ,但是實際上我會組合使用方法 1 + 2 ,在大部分甚至稍微複雜的情境都可以輕鬆應對。

End

在這最後我想要述說的是,上面的方法不是你應該使用或是可以知道的理論,而是可以馬上上手、且實戰的實作方法,如果你有更多的想法請在下面留言,一起討論。

Reference


上一篇
#7 你其實不需要在 Laravel 使用 Repository Pattern (1/2)
下一篇
#9 Authentication:讓專業的來幫你快速擴張
系列文
Laravel 擴展宇宙:從 1 到 100 十倍速打造產品獨角獸30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言