規則寫在建構子,就不可能有人繞過它。


購物車有一些「永遠不該發生」的狀態。例如:

  • 購物車裡只有加價購商品,沒有任何正常商品。
  • 購物車是空的,但還留著折扣碼跟點數折抵。
  • 某個商品的數量是 0,或是 9999。

這些規則通常會被寫在 controller:

// 常見寫法
if (empty($cart['items'])) {
    $cart['addons'] = [];
}

問題是,購物車有幾個 action?加入商品、修改數量、刪除商品、套用折扣碼、取消折扣碼、加購、改加購數量、刪加購、設定點數、填備註⋯⋯十個以上。

只要有一個 action 忘了寫這段檢查,你的 session 就髒了。而且一定會忘,因為三個月後新增的那個 action 是別人寫的。

我的做法:寫在建構子

CartInput 的建構子是 private 的,所有變更方法都必須經過它:

private function __construct(array $items, ?string $coupon, array $addOns, int $pointsToRedeem = 0, string $note = '')
{
    // invariant:沒有正常商品就不能有加購品、也不能折抵點數或留備註
    if ($items === []) {
        $addOns = [];
        $pointsToRedeem = 0;
        $note = '';
    }

    $this->items = $items;
    $this->coupon = $coupon;
    $this->pointsToRedeem = max(0, $pointsToRedeem);
    $this->note = mb_substr(trim($note), 0, self::MAX_NOTE_LENGTH);
}

而每一個變更方法,都是「改一份陣列、丟進建構子、回傳新物件」:

public function removeItem(string $sku): self
{
    $items = $this->items;
    unset($items[$sku]);

    return new self($items, $this->coupon, $this->addOns, $this->pointsToRedeem, $this->note);
}

所以不管客戶是怎麼走到「購物車沒有正常商品」這個狀態的——他可能是一件一件刪、可能是把數量改成 0、可能是商品下架被系統移除——加價購一定會被清掉,因為每一條路都會經過建構子。

這就是 immutable 物件的實際好處:它不只是「比較安全」這種空話,而是它強迫每一次變更都重新走一次驗證。可變物件做不到這件事,因為你可以直接 $cart->items = []。

三種不變條件

我會放進建構子的東西分三類:

跨欄位的一致性。 沒有商品就沒有加購。這是最重要的一類,因為它是「欄位之間的關係」,散在各處一定守不住。

上下限夾制。 數量最多 99、備註最多 1000 字、點數不能是負的。

正規化。 備註 trim、數量 0 就直接 unset 而不是存一個 0。讓同一個語意只有一種表示法,後面的程式碼就不用到處判斷 if ($qty === 0)。

這跟表單驗證不一樣

Controller 還是要做表單驗證——那是在擋「客戶送了什麼鬼東西進來」。

建構子擋的是另一件事:擋物件本身不合法。 表單驗證通過之後,資料交給 CartInput,由它保證最後存回 session 的那份,一定是一個自洽的購物車。

分工是這樣:

Request  ──驗證──▶ 合法的輸入
合法的輸入 ──建構子──▶ 合法的購物車

兩層都要,但第二層才是真正保護你 session 的那層。因為 controller 會越寫越多,建構子只有一個。


⭐️ Shopify 網站開發服務(給品牌)
https://job.turn.tw/shopify-services

⭐️ 川川電商事務所(全客製電商開發服務)
https://chuanchuan.tw

⭐️ 台灣 Shopify 商家交流 LINE 群(非官方)
https://line.me/ti/g2/PZ_1LILWVWWuzZQ50HNpYA-A3k6QXWF6znqoBQ

⭐️ 台灣 Shopify 開發者 LINE 群(非官方)
https://line.me/ti/g2/YUasX5K3CJ4QdIx76zppjHlh3-q8w-xkSyK1LA

共有 0 則留言


⭐️ Shopify 網站開發服務(給品牌)
https://job.turn.tw/shopify-services

⭐️ 川川電商事務所(全客製電商開發服務)
https://chuanchuan.tw

⭐️ 台灣 Shopify 商家交流 LINE 群(非官方)
https://line.me/ti/g2/PZ_1LILWVWWuzZQ50HNpYA-A3k6QXWF6znqoBQ

⭐️ 台灣 Shopify 開發者 LINE 群(非官方)
https://line.me/ti/g2/YUasX5K3CJ4QdIx76zppjHlh3-q8w-xkSyK1LA
🏆 本月排行榜
🥇
站長阿川
📝45   💬1  
643
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次