# 機能設計書 35-カート表示

## 概要

本ドキュメントは、Shopwareストアフロントにおけるショッピングカートページ表示機能の設計について記述する。

### 本機能の処理概要

本機能は、顧客がカートに追加した商品の一覧を表示するカートページを生成する機能である。商品の数量変更、削除、プロモーションコードの適用などの操作の起点となる画面を提供し、チェックアウトプロセスへの導線を確保する。

**業務上の目的・背景**：ECサイトにおいて、カートページは購入フローの重要なステップである。顧客が選択した商品を確認し、数量の調整やクーポン適用を行い、購入意思を固める場である。カートエラー（在庫切れ、価格変更等）の適切な表示により、顧客に必要な情報を提供し、スムーズな購入体験を実現する。

**機能の利用シーン**：
- ヘッダーのカートアイコンをクリックした際
- 商品追加後にカート表示へ遷移した際
- チェックアウト前の最終確認として
- カートエラー発生時の自動リダイレクト先

**主要な処理内容**：
1. カートデータの取得とエラーチェック
2. 利用可能な支払方法・配送方法の取得
3. 国リストの取得（ゲストユーザー向け）
4. カートエラーのフラッシュメッセージ変換
5. 配送・支払方法変更によるリダイレクト処理
6. カートページのレンダリング

**関連システム・外部連携**：
- 支払プロバイダ（支払方法の有効性確認）
- 配送プロバイダ（配送方法の有効性確認）

**権限による制御**：特になし（匿名ユーザーも利用可能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | カートページ | 主画面 | ショッピングカートの内容表示（CheckoutController::cartPage） |
| 10 | 注文確認ページ | 遷移先画面 | 注文確認画面への遷移 |

## 機能種別

データ取得・表示（Read操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| redirected | bool | No | リダイレクトループ防止フラグ | - |

### 入力データソース

- クエリパラメータ
- SalesChannelContext（セッショントークン、顧客情報、言語、通貨等）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| page | CheckoutCartPage | カートページオブジェクト |
| page.cart | Cart | カートデータ |
| page.cart.lineItems | LineItemCollection | カート内商品一覧 |
| page.cart.price | CartPrice | カート価格情報 |
| page.cart.errors | ErrorCollection | カートエラー一覧 |
| page.paymentMethods | PaymentMethodCollection | 利用可能な支払方法 |
| page.shippingMethods | ShippingMethodCollection | 利用可能な配送方法 |
| page.countries | CountryCollection | 国リスト（ゲスト用） |

### 出力先

- HTMLページ（@Storefront/storefront/page/checkout/cart/index.html.twig）
- 配送・支払方法変更時は同一ルートへリダイレクト

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ CheckoutController::cartPage()がリクエストを受信
2. CheckoutCartPageLoader呼び出し
   └─ カートページのロード処理を開始
3. GenericPageLoader呼び出し
   └─ 共通ページ情報（ヘッダー、フッター等）をロード
4. メタ情報設定
   └─ robots: noindex, タイトル設定
5. 国リスト取得（ゲストの場合）
   └─ CountryRouteで配送可能国を取得
6. カートデータ取得
   └─ StorefrontCartFacadeでカートを取得
7. チェックアウトゲートウェイ呼び出し
   └─ 支払方法・配送方法の有効性確認
8. 支払方法・配送方法設定
   └─ ページに利用可能なメソッドを設定
9. イベントディスパッチ
   └─ CheckoutCartPageLoadedEventを発行
10. フック実行
    └─ CheckoutCartPageLoadedHookを実行
11. カートエラー処理
    └─ addCartErrors()でフラッシュメッセージ変換
12. 配送・支払方法変更チェック
    └─ routeNeedsReload()でリダイレクト判定
13. テンプレートレンダリング
    └─ cart/index.html.twigをレンダリングして返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始: cartPage] --> B[CheckoutCartPageLoader::load]
    B --> C[GenericPageLoader::load]
    C --> D[メタ情報設定]
    D --> E{ログインユーザー?}
    E -->|No| F[CountryRoute::load 国リスト取得]
    E -->|Yes| G[空のCountryCollection]
    F --> H[StorefrontCartFacade::get カート取得]
    G --> H
    H --> I[CheckoutGatewayRoute::load]
    I --> J[支払・配送方法設定]
    J --> K[CheckoutCartPageLoadedEvent発行]
    K --> L[CheckoutCartPageLoadedHook実行]
    L --> M[addCartErrors フラッシュメッセージ]
    M --> N{配送・支払方法変更?}
    N -->|Yes| O{redirectedフラグ?}
    O -->|No| P[同一ルートへリダイレクト]
    O -->|Yes| Q[テンプレートレンダリング]
    N -->|No| Q
    Q --> R[終了]
    P --> A
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-35-01 | SEOインデックス除外 | カートページはnoindexを設定 | 常時 |
| BR-35-02 | キャッシュ無効 | NO_STORE属性によりキャッシュ無効 | 常時 |
| BR-35-03 | 国リスト取得条件 | ログインユーザーの場合は国リストを取得しない | ログイン時 |
| BR-35-04 | 配送方法補完 | 現在の配送方法が有効でない場合もリストに追加 | 配送方法がリストにない場合 |
| BR-35-05 | カートエラー表示 | カートエラーはフラッシュメッセージとして表示 | エラー存在時 |
| BR-35-06 | リダイレクトループ防止 | redirectedフラグでループを検知して停止 | 2回目のリダイレクト試行時 |

### 計算ロジック

**リダイレクト判定（routeNeedsReload）**:
- ShippingMethodChangedError または PaymentMethodChangedError が存在する場合、リダイレクト必要
- redirectedフラグがtrueの場合はリダイレクトしない（ループ防止）

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カート取得 | cart | SELECT | セッショントークンに紐づくカートを取得 |
| 支払方法取得 | payment_method | SELECT | 有効な支払方法を取得 |
| 配送方法取得 | shipping_method | SELECT | 有効な配送方法を取得 |
| 国リスト取得 | country | SELECT | 配送可能国を取得 |

### テーブル別操作詳細

#### cart

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | token, payload, price, line_items, errors | token = {セッショントークン} AND sales_channel_id = {販売チャネルID} | Redis使用時はRedisから取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SHIPPING_METHOD_CHANGED | 配送方法変更 | 選択中の配送方法が無効になった場合 | 代替配送方法に自動変更、リダイレクト |
| PAYMENT_METHOD_CHANGED | 支払方法変更 | 選択中の支払方法が無効になった場合 | 代替支払方法に自動変更、リダイレクト |
| PRODUCT_NOT_FOUND | 商品エラー | カート内商品が削除された場合 | エラーメッセージ表示 |
| INSUFFICIENT_STOCK | 在庫不足 | 商品の在庫が不足している場合 | エラーメッセージ表示 |

### リトライ仕様

特になし（読み取り専用処理のためリトライ不要）

## トランザクション仕様

読み取り専用処理のため、トランザクション制御なし

## パフォーマンス要件

- レスポンス時間: 500ms以内（通常時）
- カートデータはRedis等でキャッシュ（設定による）
- NO_STORE属性によりHTTPキャッシュ無効

## セキュリティ考慮事項

- セッショントークンによるカート分離
- XSS対策としてテンプレート側でエスケープ処理
- カートページはnoindexを設定し、外部からのインデックス防止

## 備考

- カートが空の場合も正常にページ表示
- カートエラーはページ表示後にクリアされる

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、カート機能で使用されるデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | CheckoutCartPage.php | `src/Storefront/Page/Checkout/Cart/CheckoutCartPage.php` | カートページの構造 |
| 1-2 | Cart.php | `src/Core/Checkout/Cart/Cart.php` | カートエンティティの構造 |
| 1-3 | LineItem.php | `src/Core/Checkout/Cart/LineItem/LineItem.php` | カート明細の構造 |
| 1-4 | CartPrice.php | `src/Core/Checkout/Cart/Price/Struct/CartPrice.php` | カート価格の構造 |

**読解のコツ**: CheckoutCartPageはcart、paymentMethods、shippingMethods、countriesを保持する。Cartはerrors（ErrorCollection）を持ち、カートの問題を検出・通知する。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | CheckoutController.php | `src/Storefront/Controller/CheckoutController.php` | cartPageメソッドがエントリーポイント |

**主要処理フロー**:
1. **74-80行目**: ルート定義（/checkout/cart, NO_STORE属性）
2. **81行目**: cartPageメソッドの開始
3. **83行目**: CheckoutCartPageLoaderの呼び出し
4. **87行目**: CheckoutCartPageLoadedHookの実行
5. **89行目**: addCartErrorsの呼び出し
6. **91-99行目**: routeNeedsReloadによるリダイレクト判定
7. **102行目**: テンプレートレンダリング

#### Step 3: ページローダーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | CheckoutCartPageLoader.php | `src/Storefront/Page/Checkout/Cart/CheckoutCartPageLoader.php` | カートページロード処理 |

**主要処理フロー**:
- **45-74行目**: load()メソッド - カートページ生成のメイン処理
- **47-48行目**: GenericPageLoaderで共通情報取得
- **52行目**: 国リスト取得（getCountries）
- **54行目**: StorefrontCartFacadeでカート取得
- **56行目**: CheckoutGatewayRouteで支払・配送方法取得
- **58-59行目**: 支払方法・カート設定
- **61-66行目**: 配送方法設定（現在の配送方法を補完）
- **69-71行目**: イベントディスパッチ
- **84-95行目**: getCountries() - 国リスト取得ロジック

#### Step 4: リダイレクト判定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | CheckoutController.php | `src/Storefront/Controller/CheckoutController.php` | routeNeedsReloadメソッド |

**主要処理フロー**:
- **323-332行目**: routeNeedsReload() - リダイレクト要否判定
- **325-329行目**: ShippingMethodChangedErrorまたはPaymentMethodChangedErrorの検出

### プログラム呼び出し階層図

```
CheckoutController::cartPage()
    │
    ├─ CheckoutCartPageLoader::load()
    │      │
    │      ├─ GenericPageLoader::load() [共通ページ情報]
    │      │
    │      ├─ setMetaInformation() [SEO設定]
    │      │
    │      ├─ getCountries() [国リスト取得]
    │      │      └─ CountryRoute::load()
    │      │
    │      ├─ StorefrontCartFacade::get() [カート取得]
    │      │
    │      ├─ CheckoutGatewayRoute::load() [支払・配送方法]
    │      │
    │      └─ EventDispatcher::dispatch() [CheckoutCartPageLoadedEvent]
    │
    ├─ hook() [CheckoutCartPageLoadedHook]
    │
    ├─ addCartErrors() [フラッシュメッセージ変換]
    │
    ├─ routeNeedsReload() [リダイレクト判定]
    │
    └─ renderStorefront() [テンプレートレンダリング]
```

### データフロー図

```
[入力]                    [処理]                         [出力]

セッショントークン ──────▶ CheckoutCartPageLoader ──────▶ CheckoutCartPage
                              │                              │
                              ├─ StorefrontCartFacade        ├─ cart
                              │      │                       │   ├─ lineItems
                              │      ▼                       │   ├─ price
                              │  cart テーブル               │   └─ errors
                              │  (Redis)                     │
                              │                              ├─ paymentMethods
                              ├─ CheckoutGatewayRoute        ├─ shippingMethods
                              │      │                       └─ countries
                              │      ▼                              │
                              │  payment_method                     ▼
                              │  shipping_method        cart/index.html.twig
                              │                                     │
                              └─ CountryRoute                       ▼
                                     │                       HTMLレスポンス
                                     ▼                       またはリダイレクト
                                 country
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| CheckoutController.php | `src/Storefront/Controller/CheckoutController.php` | コントローラー | HTTPリクエスト処理 |
| CheckoutCartPageLoader.php | `src/Storefront/Page/Checkout/Cart/CheckoutCartPageLoader.php` | ページローダー | カートページデータ取得 |
| CheckoutCartPage.php | `src/Storefront/Page/Checkout/Cart/CheckoutCartPage.php` | DTO | カートページデータ構造 |
| StorefrontCartFacade.php | `src/Storefront/Checkout/Cart/SalesChannel/StorefrontCartFacade.php` | ファサード | カート取得 |
| Cart.php | `src/Core/Checkout/Cart/Cart.php` | エンティティ | カートデータ |
| LineItem.php | `src/Core/Checkout/Cart/LineItem/LineItem.php` | エンティティ | カート明細 |
| ShippingMethodChangedError.php | `src/Storefront/Checkout/Cart/Error/ShippingMethodChangedError.php` | エラー | 配送方法変更エラー |
| PaymentMethodChangedError.php | `src/Storefront/Checkout/Cart/Error/PaymentMethodChangedError.php` | エラー | 支払方法変更エラー |
| index.html.twig | `src/Storefront/Resources/views/storefront/page/checkout/cart/index.html.twig` | テンプレート | カートページビュー |
| CheckoutCartPageLoadedHook.php | `src/Storefront/Page/Checkout/Cart/CheckoutCartPageLoadedHook.php` | フック | 拡張ポイント |
