# 機能設計書 39-商品追加

## 概要

本ドキュメントは、Shopwareストアフロントにおけるカートへの商品追加機能の設計について記述する。

### 本機能の処理概要

本機能は、顧客が選択した商品をショッピングカートに追加する機能である。商品詳細ページやリスティングページから呼び出され、指定した商品と数量をカートに追加する。複数商品の同時追加にも対応している。

**業務上の目的・背景**：ECサイトの中核機能であり、顧客の購買行動を支える最重要機能の一つである。商品をカートに追加することで購入フローが開始される。商品追加時のフィードバック（成功メッセージ、エラー表示）を適切に行うことで、顧客体験を向上させる。

**機能の利用シーン**：
- 商品詳細ページで「カートに追加」ボタンをクリックした際
- 商品一覧ページでクイック追加ボタンをクリックした際
- クイックビューモーダルから商品を追加した際
- ウィッシュリストから商品をカートに移動した際

**主要な処理内容**：
1. 追加対象の商品データ（lineItems）の受け取り
2. 各商品のLineItemオブジェクト生成
3. CartServiceを通じたカートへの追加
4. カートエラーの検出とフラッシュメッセージ変換
5. 成功/エラーメッセージの表示
6. アクションレスポンスの生成（リダイレクトまたはAjax応答）

**関連システム・外部連携**：
- 在庫管理システム（在庫チェック）
- 価格計算エンジン（価格確定）

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 3 | 商品詳細ページ | 主画面 | カートへの商品追加（CartLineItemController::addLineItems） |
| 4 | 商品クイックビュー | 主画面 | カートへの商品追加 |

## 機能種別

データ作成・更新（Create/Update操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| lineItems | array | Yes | 追加する商品データの配列 | 必須、配列形式 |
| lineItems[*].id | string | Yes | 商品ID | UUID形式 |
| lineItems[*].type | string | Yes | 明細タイプ（product等） | サポートされたタイプ |
| lineItems[*].quantity | int | No | 数量（デフォルト: 1） | 正の整数 |
| lineItems[*].referencedId | string | No | 参照ID（商品ID） | UUID形式 |
| lineItems[*].stackable | bool | No | 積み重ね可能フラグ（デフォルト: true） | true/false |
| lineItems[*].removable | bool | No | 削除可能フラグ（デフォルト: true） | true/false |
| lineItems[*].payload | array | No | 追加ペイロード | JSON形式 |

### 入力データソース

- POSTリクエストボディ（RequestDataBag）
- Cart（現在のカート）
- SalesChannelContext（セッション情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| (フラッシュメッセージ) | string | 成功/エラーメッセージ |

### 出力先

- アクションレスポンス（リダイレクトまたはAjax JSON応答）
- フラッシュメッセージ（セッション）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ CartLineItemController::addLineItems()がPOSTリクエストを受信
2. lineItemsパラメータ取得
   └─ RequestDataBagからlineItemsを取得
3. バリデーション
   └─ lineItemsが存在しない場合はRoutingExceptionをスロー
4. LineItemオブジェクト生成ループ
   └─ 各lineItemDataに対してLineItemFactoryRegistryでLineItemを生成
5. 数量バリデーション
   └─ 不正な数量の場合はCART_INVALID_LINE_ITEM_QUANTITY_CODEエラー
6. カートへの追加
   └─ CartService::add()でカートに追加
7. エラーチェック
   └─ traceErrors()でカートエラーをフラッシュメッセージに変換
8. 成功メッセージ表示
   └─ エラーがなければ成功メッセージを追加
9. アクションレスポンス生成
   └─ createActionResponse()でリダイレクトまたはAjax応答
```

### フローチャート

```mermaid
flowchart TD
    A[開始: addLineItems POST] --> B[lineItemsパラメータ取得]
    B --> C{lineItems存在?}
    C -->|No| D[RoutingException: missingRequestParameter]
    C -->|Yes| E[foreach lineItemsループ開始]
    E --> F[LineItemFactoryRegistry::create]
    F --> G{数量エラー?}
    G -->|Yes| H[数量エラーメッセージ追加]
    H --> I[アクションレスポンス返却]
    G -->|No| J[items配列に追加]
    J --> K{次のlineItem?}
    K -->|Yes| E
    K -->|No| L[CartService::add]
    L --> M[traceErrors カートエラー処理]
    M --> N{エラーあり?}
    N -->|Yes| O[エラーメッセージ表示]
    N -->|No| P[成功メッセージ追加]
    O --> Q[アクションレスポンス生成]
    P --> Q
    I --> R[終了]
    Q --> R
    D --> R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-39-01 | lineItems必須 | lineItemsパラメータは必須 | 常時 |
| BR-39-02 | デフォルト数量 | 数量未指定時は1を設定 | quantity未指定時 |
| BR-39-03 | デフォルト積み重ね | stackable未指定時はtrueを設定 | stackable未指定時 |
| BR-39-04 | デフォルト削除可 | removable未指定時はtrueを設定 | removable未指定時 |
| BR-39-05 | 複数商品対応 | 複数のlineItemを一度に追加可能 | 常時 |
| BR-39-06 | 数量バリデーション | 不正な数量（0以下、最大値超過）はエラー | 常時 |
| BR-39-07 | 商品存在チェック | 存在しない商品IDはエラー | 常時 |
| BR-39-08 | Ajax対応 | XmlHttpRequestの場合はJSON形式で応答 | XmlHttpRequest時 |

### 計算ロジック

**追加数量の計算**:
- 同一商品が既にカートにある場合は数量を加算
- stackable=trueの場合のみ加算可能
- stackable=falseの場合は別明細として追加

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 商品検証 | product | SELECT | 商品の存在と有効性を確認 |
| カート更新 | cart | UPDATE | カートに商品を追加 |

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

#### cart

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | payload, line_items, price, updated_at | 新しい明細を追加、価格を再計算 | Redis使用時はRedisを更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| MISSING_REQUEST_PARAMETER | パラメータ不足 | lineItemsが存在しない場合 | エラーメッセージ表示 |
| CART_INVALID_LINE_ITEM_QUANTITY | 数量エラー | 数量が不正な場合 | エラーメッセージ表示 |
| PRODUCT_NOT_FOUND | 商品エラー | 商品が存在しない場合 | エラーメッセージ表示 |
| LINE_ITEM_TYPE_NOT_SUPPORTED | タイプエラー | サポートされないタイプの場合 | エラーメッセージ表示 |

### リトライ仕様

特になし（ユーザー操作による再試行）

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

- カート更新は単一トランザクションで実行
- エラー発生時はロールバック

## パフォーマンス要件

- レスポンス時間: 500ms以内
- 複数商品追加時も一括処理で効率化
- Profiler::traceによるパフォーマンス計測対応

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

- セッショントークンによるカート分離
- XSS対策としてフラッシュメッセージをエスケープ
- SQLインジェクション対策としてDAL使用
- payload のサイズ制限（256KB上限）

## 備考

- Profiler::trace('cart::add-line-item')でパフォーマンス計測
- 商品追加成功後はオフキャンバスカートが自動表示（フロントエンド制御）

---

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

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

### 推奨読解順序

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

まず、商品追加で使用されるデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | LineItem.php | `src/Core/Checkout/Cart/LineItem/LineItem.php` | カート明細の構造（id, type, quantity等） |
| 1-2 | RequestDataBag.php | `src/Core/Framework/Validation/DataBag/RequestDataBag.php` | リクエストデータの構造 |

**読解のコツ**: LineItemはid、type、quantity、payload等のプロパティを持つ。typeはproduct、promotionなどの種別を表す。

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

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

**主要処理フロー**:
1. **279行目**: ルート定義（/checkout/line-item/add, XmlHttpRequest対応）
2. **280行目**: addLineItemsメソッドの開始
3. **281-287行目**: Profiler::trace内でlineItems取得とバリデーション
4. **292-321行目**: foreachループでLineItem生成
5. **296-300行目**: LineItemFactoryRegistry::createでLineItem生成
6. **304-316行目**: 数量エラーハンドリング
7. **323行目**: CartService::addでカートに追加
8. **325-327行目**: エラーなければ成功メッセージ追加
9. **332行目**: createActionResponseでレスポンス生成

#### Step 3: LineItem生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | LineItemFactoryRegistry.php | `src/Core/Checkout/Cart/LineItemFactoryRegistry.php` | LineItem生成の統括 |
| 3-2 | ProductLineItemFactory.php | `src/Core/Checkout/Cart/LineItemFactoryHandler/ProductLineItemFactory.php` | 商品LineItem生成 |

**主要処理フロー**:
- **44-58行目**: create()メソッド - LineItem生成のメイン処理
- **52行目**: getHandler()でタイプに応じたファクトリを取得
- **54行目**: handler.create()でLineItemを生成
- **55行目**: markModified()で変更フラグを設定

#### Step 4: カートサービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | CartService.php | `src/Core/Checkout/Cart/SalesChannel/CartService.php` | カートへの追加処理 |

**主要処理フロー**:
- **89-98行目**: add()メソッド - カート追加のメイン処理
- **95行目**: itemAddRouteでStore-APIルートに委譲
- **97行目**: キャッシュを更新

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

```
CartLineItemController::addLineItems()
    │
    ├─ Profiler::trace('cart::add-line-item')
    │
    ├─ requestDataBag->get('lineItems') [パラメータ取得]
    │
    ├─ foreach lineItems
    │      │
    │      ├─ getLineItemArray() [データ整形]
    │      │
    │      └─ LineItemFactoryRegistry::create() [LineItem生成]
    │             │
    │             ├─ getHandler() [ファクトリ選択]
    │             │      └─ ProductLineItemFactory
    │             │
    │             └─ handler.create() [LineItem作成]
    │
    ├─ CartService::add() [カート追加]
    │      │
    │      └─ AbstractCartItemAddRoute::add()
    │             │
    │             └─ CartCalculator::calculate()
    │
    ├─ traceErrors() [エラー処理]
    │      └─ filterSuccessErrorMessages()
    │
    └─ createActionResponse() [レスポンス生成]
```

### データフロー図

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

lineItems[] ─────────▶ LineItemFactoryRegistry ──────▶ LineItem[]
   │                         │                              │
   ├─ id                     ▼                              │
   ├─ type           ProductLineItemFactory                 │
   ├─ quantity               │                              ▼
   ├─ referencedId           ▼                       CartService::add
   ├─ stackable        LineItem                              │
   ├─ removable              │                              ▼
   └─ payload                ▼                      cart テーブル
                    CartItemAddRoute                   (Redis)
                             │                              │
                             ▼                              ▼
                    CartCalculator                   ActionResponse
                    (価格再計算)                    (リダイレクト/JSON)
                             │
                             ▼
                      フラッシュメッセージ
                    (成功/エラー)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| CartLineItemController.php | `src/Storefront/Controller/CartLineItemController.php` | コントローラー | HTTPリクエスト処理 |
| LineItemFactoryRegistry.php | `src/Core/Checkout/Cart/LineItemFactoryRegistry.php` | レジストリ | LineItem生成の統括 |
| ProductLineItemFactory.php | `src/Core/Checkout/Cart/LineItemFactoryHandler/ProductLineItemFactory.php` | ファクトリ | 商品LineItem生成 |
| CartService.php | `src/Core/Checkout/Cart/SalesChannel/CartService.php` | サービス | カート操作 |
| LineItem.php | `src/Core/Checkout/Cart/LineItem/LineItem.php` | エンティティ | カート明細 |
| Cart.php | `src/Core/Checkout/Cart/Cart.php` | エンティティ | カートデータ |
| RequestDataBag.php | `src/Core/Framework/Validation/DataBag/RequestDataBag.php` | DTO | リクエストデータ |
| add-to-cart.plugin.js | `src/Storefront/Resources/app/storefront/src/plugin/add-to-cart/add-to-cart.plugin.js` | JavaScript | フロントエンド商品追加処理 |
