# 画面設計書 3-商品詳細ページ

## 概要

本ドキュメントは、Shopware 6 Storefrontにおける商品詳細ページの画面設計書である。商品の詳細情報、画像、価格、購入オプション、レビューなどを表示し、カート追加機能を提供する重要な画面である。

### 本画面の処理概要

商品詳細ページは、個々の商品に関する詳細情報を表示し、顧客が購入判断を行うための情報を提供する画面である。CMSベースのレイアウトで構成され、バリアント商品の切り替えやレビュー機能も統合されている。

**業務上の目的・背景**：商品詳細ページはECサイトにおける「購買決定」の重要な接点である。商品の魅力を最大限に伝え、顧客の購入意欲を高めるために、高品質な商品画像、詳細な説明文、価格情報、在庫状況、レビューなどを効果的に表示する。バリアント商品（色違い、サイズ違い等）のスムーズな切り替えにより、ユーザー体験を向上させている。

**画面へのアクセス方法**：カテゴリページの商品カードクリック、検索結果からの商品選択、URL直接アクセス（`/detail/{productId}`またはSEO URL）、クイックビューからの「詳細を見る」リンクでアクセスする。

**主要な操作・処理内容**：
1. 商品情報の読み込みと表示（ProductPageLoader）
2. 商品画像ギャラリーの表示（スワイプ対応）
3. バリアント選択と商品切り替え（AJAX）
4. 数量選択とカートへの追加
5. お気に入りへの追加（ログインユーザー）
6. レビューの表示と投稿

**画面遷移**：
- 遷移元：カテゴリページ、検索結果ページ、ウィッシュリスト、クイックビュー
- 遷移先：カートページ、オフキャンバスカート、他のバリアント商品、レビュー投稿フォーム

**権限による表示制御**：基本的に全ユーザーがアクセス可能。レビュー投稿はログインが必要。お気に入り機能はログイン状態により表示が変化する。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | 商品詳細表示 | 主機能 | 商品の詳細情報、画像、オプションの表示（ProductController::index） |
| 28 | バリアント切替 | 補助機能 | サイズや色などのバリアント切り替え（ProductController::switch） |
| 30 | レビュー投稿 | 補助機能 | 商品レビューの投稿処理（ProductController::saveReview） |
| 31 | レビュー表示 | 補助機能 | 商品レビュー一覧の表示（ProductController::loadReviews） |
| 39 | 商品追加 | 遷移先機能 | カートへの商品追加（CartLineItemController::addLineItems） |
| 51 | お気に入り追加 | API連携 | お気に入りへの追加（WishlistController::ajaxAdd） |

## 画面種別

詳細

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| URL | `/detail/{productId}` |
| SEO URL | 商品設定に基づく（例: `/product-name/`） |
| ルート名 | `frontend.detail.page` |
| HTTPメソッド | GET |
| HTTPキャッシュ | 有効 |

## 入出力項目

### 入力項目

| 項目名 | パラメータ名 | 必須 | 型 | 説明 |
|--------|-------------|------|-----|------|
| 商品ID | productId | 必須 | string(UUID) | 商品のID |

### 出力データ

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| page | ProductPageLoader | ページ全体のデータオブジェクト |
| page.product | 商品テーブル | 商品エンティティ（価格、在庫、メディア等含む） |
| page.cmsPage | CMS設定 | CMSページレイアウト情報 |
| page.reviews | レビューテーブル | 商品レビュー一覧 |
| page.configuratorSettings | 設定テーブル | バリアント設定情報 |

## 表示項目

| セクション | 表示項目 | 説明 |
|-----------|---------|------|
| ヘッダー | 共通ヘッダー | ロゴ、ナビゲーション、検索、カート |
| パンくず | カテゴリ階層 | 商品のSEOカテゴリに基づく階層表示 |
| メイン | 商品画像ギャラリー | メイン画像とサムネイル一覧 |
| メイン | 商品名 | 商品タイトル |
| メイン | メーカー名 | ブランド/メーカー情報 |
| メイン | 価格表示 | 通常価格、セール価格、単価表示 |
| メイン | 在庫状況 | 在庫あり/なし、納期情報 |
| メイン | バリアント選択 | 色、サイズ等のオプション選択 |
| メイン | 数量入力 | 購入数量の選択 |
| メイン | カート追加ボタン | 「カートに入れる」ボタン |
| メイン | お気に入りボタン | ウィッシュリスト追加ボタン |
| 詳細 | 商品説明 | 詳細な説明文（HTML対応） |
| 詳細 | 商品プロパティ | 仕様、寸法等のプロパティ一覧 |
| レビュー | レビュー一覧 | 顧客レビューと評価星 |
| レビュー | レビュー投稿フォーム | 新規レビュー入力フォーム |
| フッター | 共通フッター | フッターリンク |

## イベント仕様

### 1-商品詳細ページ表示

商品URLにアクセスすると、ProductController::indexメソッドが実行される。

**処理フロー**：
1. ProductPageLoaderがリクエストとSalesChannelContextを受け取る
2. productIdから商品エンティティを取得（価格計算含む）
3. CMSページレイアウトを解決
4. バリアント設定情報を取得
5. ProductPageLoadedHookを発火
6. Twigテンプレートでレンダリング

### 2-バリアント切替

バリアントオプション（色、サイズ等）を選択すると、AJAX経由で新しい商品IDが取得される。

**エンドポイント**: `/detail/{productId}/switch`
**ルート名**: `frontend.detail.switch`
**HTTPメソッド**: GET

**処理フロー**：
1. switchedGroup（変更されたオプショングループ）とoptions（選択済みオプション）を受け取る
2. FindProductVariantRouteで該当するバリアントを検索
3. 新しい商品IDとURLをJSONで返却
4. フロントエンドがURLに基づいてページを更新

### 3-カート追加

「カートに入れる」ボタン押下時、選択した商品がカートに追加される。

**エンドポイント**: `/checkout/line-item/add`
**ルート名**: `frontend.checkout.line-item.add`
**HTTPメソッド**: POST

### 4-レビュー投稿

レビューフォームを送信すると、レビューが保存される。

**エンドポイント**: `/product/{productId}/rating`
**ルート名**: `frontend.detail.review.save`
**HTTPメソッド**: POST
**必須条件**: ログイン必須

### 5-レビュー読み込み

レビューセクションの読み込みまたはページング時にレビュー一覧を取得する。

**エンドポイント**: `/product/{productId}/reviews`
**ルート名**: `frontend.product.reviews`
**HTTPメソッド**: GET/POST

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ページ表示 | product, product_media, category 等 | SELECT | 商品データ参照 |
| バリアント切替 | product | SELECT | バリアント検索 |
| カート追加 | cart, cart_line_item | INSERT/UPDATE | カートへ商品追加 |
| レビュー投稿 | product_review | INSERT | レビュー保存 |
| お気に入り追加 | customer_wishlist_product | INSERT | お気に入り保存 |

### テーブル別更新項目詳細

#### product_review（レビュー投稿時）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | product_id | リクエストのproductId | 対象商品 |
| INSERT | customer_id | ログインユーザーID | 投稿者 |
| INSERT | sales_channel_id | 現在の販売チャネル | |
| INSERT | language_id | 現在の言語 | |
| INSERT | title | フォーム入力値 | レビュータイトル |
| INSERT | content | フォーム入力値 | レビュー本文 |
| INSERT | points | フォーム入力値（1-5） | 評価点数 |
| INSERT | status | システム設定による | 承認ステータス |

## メッセージ仕様

| メッセージ種別 | 条件 | メッセージ内容 |
|--------------|------|--------------|
| 成功 | カート追加成功 | 「{count}個の商品がカートに追加されました」 |
| 成功 | レビュー投稿成功 | 「レビューが投稿されました」 |
| 成功 | レビュー更新成功 | 「レビューが更新されました」 |
| エラー | 商品未発見 | 404エラーページ表示 |
| エラー | バリアント未発見 | 現在の商品ページを維持 |
| エラー | レビュー投稿エラー | バリデーションエラーメッセージ表示 |

## 例外処理

| 例外条件 | 対応 |
|---------|------|
| 商品未発見（ProductNotFoundException） | 404エラーページへリダイレクト |
| バリアント未発見（VariantNotFoundException） | 現在の商品IDを維持してレスポンス |
| レビュー機能無効（ReviewNotActiveExeption） | StorefrontException::reviewNotActive |
| レビュー投稿時のバリデーションエラー | フォームエラー表示 |

## 備考

- 商品画像はメディアギャラリーとして複数登録可能
- 価格は販売チャネル、顧客グループ、通貨に基づいて計算される
- バリアント切替はAJAXで行われ、ページ全体のリロードは発生しない
- レビュー投稿には管理画面での承認が必要な場合がある（設定による）
- Schema.org のProduct構造化データがマークアップされている

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ProductPage.php | `src/Storefront/Page/Product/ProductPage.php` | ページオブジェクトの構造、productプロパティ |
| 1-2 | SalesChannelProductEntity.php | `src/Core/Content/Product/SalesChannel/SalesChannelProductEntity.php` | 価格計算済み商品エンティティ |

**読解のコツ**: SalesChannelProductEntityは通常のProductEntityを拡張し、calculatedPrice等の計算済み価格情報を持つ。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ProductController.php | `src/Storefront/Controller/ProductController.php` | indexメソッド（53-72行目）がメイン処理 |

**主要処理フロー**:
1. **59-61行目**: ProductPageLoaderで商品データ取得
2. **63行目**: ProductPageLoadedHookでフック処理
3. **65-71行目**: テンプレートレンダリングとredirectTo設定

#### Step 3: バリアント切替を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ProductController.php | `src/Storefront/Controller/ProductController.php` | switchメソッド（74-128行目） |

**主要処理フロー**:
- **85-91行目**: switchedGroupとoptionsの取得
- **100-107行目**: FindProductVariantRouteでバリアント検索
- **112-122行目**: SEO URLの生成
- **124-127行目**: JSONレスポンス（URL、productId）

#### Step 4: レビュー機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ProductController.php | `src/Storefront/Controller/ProductController.php` | saveReviewメソッド（145-194行目） |
| 4-2 | ProductController.php | `src/Storefront/Controller/ProductController.php` | loadReviewsメソッド（196-229行目） |

#### Step 5: テンプレート構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | product-detail.html.twig | `src/Storefront/Resources/views/storefront/page/content/product-detail.html.twig` | メインテンプレート |

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

```
ProductController::index()
    |
    +-- ProductPageLoader::load()
    |       |
    |       +-- ProductDetailRoute::load()
    |       |       +-- ProductPriceCalculator
    |       |
    |       +-- CmsPageLoader::load()
    |       |
    |       +-- ProductConfiguratorLoader
    |
    +-- ProductPageLoadedHook
    |
    +-- renderStorefront()
            +-- product-detail.html.twig

ProductController::switch()
    |
    +-- FindProductVariantRoute::load()
    |       +-- VariantCombinationLoader
    |
    +-- SeoUrlPlaceholderHandler
    |
    +-- JsonResponse (url, productId)

ProductController::saveReview()
    |
    +-- ProductReviewSaveRoute::save()
            +-- product_review INSERT
```

### データフロー図

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

productId         -->  ProductController::index()
    |                       |
    v                       v
Request               ProductPageLoader
                           |
                           v
                      ProductDetailRoute
                           |
                           +-- 価格計算
                           +-- メディア取得
                           +-- バリアント設定
                           |
                           v
                      ProductPage          -->  HTML Response
                           |
                           v
                      product-detail.html.twig
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ProductController.php | `src/Storefront/Controller/ProductController.php` | コントローラー | ルーティングエントリーポイント |
| ProductPageLoader.php | `src/Storefront/Page/Product/ProductPageLoader.php` | ページローダー | ページデータの構築 |
| ProductPage.php | `src/Storefront/Page/Product/ProductPage.php` | データ構造 | ページオブジェクト定義 |
| product-detail.html.twig | `src/Storefront/Resources/views/storefront/page/content/product-detail.html.twig` | テンプレート | メインテンプレート |
| buy-widget.html.twig | `src/Storefront/Resources/views/storefront/component/buy-widget/buy-widget.html.twig` | テンプレート | 購入ウィジェット |
| configurator.html.twig | `src/Storefront/Resources/views/storefront/component/buy-widget/configurator.html.twig` | テンプレート | バリアント選択 |
| review.html.twig | `src/Storefront/Resources/views/storefront/component/review/review.html.twig` | テンプレート | レビュー一覧 |
| review-form.html.twig | `src/Storefront/Resources/views/storefront/component/review/review-form.html.twig` | テンプレート | レビュー投稿フォーム |
| meta.html.twig | `src/Storefront/Resources/views/storefront/page/product-detail/meta.html.twig` | テンプレート | SEOメタタグ |
