Laravel Pennant 介紹

前天(2023/8/18)完成了 Laravel Pennant 的工作坊,今天來分享一下這個套件的介紹。

Laravel Pennant 能做什麼事?

Laravel Pennant 主要功能是管理 Feature 與 Scope 組合後的狀態表。例如下面的表格:

Feature Scope Active
awesome_feature miles ON
awesome_feature john OFF
awesome_feature null OFF
another_feature miles OFF
team_feature miles_team ON

Scope 的定義是抽象的,沒有明確規定要如何對應,它代表的是一個被標示的實體(identified entity)。實務上的範例如,可以對應到人,或者是團隊等。像上表的 miles 就能夠使用 awesome_feature 功能,而 john 則不行。

Scope 也可以是 null,不指定通常就代表著全域功能切換。

控制 Feature 狀態

Laravel Pennant 提供四個公開方法讓開發者可以控制狀態:

  1. 單一 scope 更新
Feature::for($object)->activate($feature);
  1. 批量 scope 更新
// $objects 為 array
Feature::for($objects)->activate($feature);
  1. 現有 DB 所保存的 scope 狀態更新
Feature::activateForEveryone($feature);
  1. 清除狀態
Feature::purge($feature);

Laravel Pennant 原始碼分析

其實 Laravel Pennant 官網裡面說明寫得很詳細,接著來看看裡面的架構。

classDiagram
class FeatureManager
class PendingScopedFeatureInteraction
class Driver
<<Interface>> Driver
class ArrayDriver
class DatabaseDriver
class Decorator
Driver <|-- ArrayDriver
Driver <|-- DatabaseDriver
Driver <|-- Decorator
Decorator o-- Driver
FeatureManager *-- Driver
Decorator --> PendingScopedFeatureInteraction
PendingScopedFeatureInteraction o-- Driver

管理 Feature 的主要物件是 FeatureManager,而產生 Driver 的時候會使用 Decorator 包裝(在 resolve() 方法裡):

return new Decorator(
$name,
$driver,
$this->defaultScopeResolver($name),
$this->container,
new Collection
);

Laravel 設計 Manager 的習慣,是它把當作 Aggregate Root 在做的,因此所有存取背後的 Entity 都必須要經過它。

實際在使用 FeatureManager 的時候,可以透過 __call() 直接 Proxy 到預設的 Driver:

public function __call($method, $parameters)
{
return $this->store()->$method(...$parameters);
}

有需要的話再使用 store() 方法指定 driver。

最後是 PendingScopedFeatureInteraction 類別。它的用途是讓 Decorator 整合 Scope 參數的邏輯整理在這個 class,而原本的 Decorator 則是專注在跟 Driver 互動,這是一種職責分離的處理方法。(Container 也有一樣的設計)

而對廣義的程式設計來說,這是一種 Mixin 的手法:

FeatureManager mixin Decorator
Decorator mixin PendingScopedFeatureInteraction

這個結果也就讓 FeatureManager 可以同時擁有 Decorator 與 PendingScopedFeatureInteraction 所提供的方法。但架構上還是如同前面提到的:Laravel 設計 Manager 的概念,就是把 Manager 當作是 Aggregate Root。