# 機能設計書 58-カテゴリページ表示

## 概要

本ドキュメントは、Shopware Storefrontにおける「カテゴリページ表示」機能の設計仕様を定義する。商品カテゴリのナビゲーションページを表示し、CMSレイアウトで構成されたコンテンツや商品一覧を顧客に提供する。

### 本機能の処理概要

**業務上の目的・背景**：ECサイトにおいて、カテゴリ階層によるナビゲーションは顧客が目的の商品を見つけるための主要な手段である。本機能は、各カテゴリに紐付くCMSページをレンダリングし、商品一覧やカテゴリ説明などを表示する。また、リンクタイプのカテゴリの場合は適切なURLへリダイレクトする。

**機能の利用シーン**：顧客がナビゲーションメニューやパンくずリスト、検索結果からカテゴリリンクをクリックした際に本機能が呼び出される。カテゴリURLにはSEOフレンドリーURLが使用される。

**主要な処理内容**：
1. リクエストからナビゲーションIDを取得
2. カテゴリ情報とCMSページをロード
3. カテゴリタイプがリンクの場合はリダイレクト
4. メタ情報（SEO）を設定
5. Canonicalタグを設定
6. NavigationPageLoadedEventイベント発火
7. CMSレイアウトをレンダリング

**関連システム・外部連携**：本機能は Store API の `CategoryRoute` を内部的に利用し、カテゴリとCMSページ情報を取得する。`NavigationPageLoadedEvent`イベントを発火し、プラグインによる拡張を可能にする。

**権限による制御**：本機能はログイン不要で利用可能。ただし、カテゴリの公開設定、顧客グループによるアクセス制限が適用される場合がある。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | カテゴリページ | 主画面 | カテゴリナビゲーションと商品一覧の表示 |

## 機能種別

ページ表示 / CMS連携 / SEO対応 / リダイレクト

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| navigationId | string (UUID) | Yes | カテゴリのID | UUID形式、アクティブなカテゴリであること |
| p | int | No | ページネーション用ページ番号 | 正の整数 |

### 入力データソース

- URLパス: `/navigation/{navigationId}` またはSEO URL
- SalesChannelContext: 販売チャネル情報、顧客情報を含む

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| page | NavigationPage | ナビゲーションページオブジェクト（CMSPage含む） |

### 出力先

- HTTPレスポンス: Twigテンプレートによるページ表示
- リンクタイプカテゴリの場合: 301/302リダイレクト

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ NavigationController::index が呼び出される

2. NavigationPageLoader::load 実行
   └─ GenericPageLoaderで共通ページ情報をロード
   └─ NavigationPageを作成

3. ナビゲーションID取得
   └─ リクエストからnavigationIdを取得

4. カテゴリ情報取得
   └─ CategoryRoute::load でカテゴリとCMSページを取得

5. アクティブ状態チェック
   └─ カテゴリが非アクティブの場合はCategoryNotFound例外

6. カテゴリタイプチェック
   └─ TYPE_LINKの場合はリダイレクト処理

7. メタ情報設定
   └─ カテゴリのメタ情報を設定

8. CMSページ設定
   └─ カテゴリに紐付くCMSPageをページに設定

9. Canonical設定
   └─ NavigationPageSeoUrlRoute用のURLを生成
   └─ ページネーションがある場合は?p=パラメータを追加

10. イベント発火
    └─ NavigationPageLoadedEvent をディスパッチ

11. レスポンス返却
    └─ index.html.twig でCMSレイアウトをレンダリング
```

### フローチャート

```mermaid
flowchart TD
    A[開始: index呼び出し] --> B[NavigationPageLoader::load]
    B --> C[GenericPageLoader::load]
    C --> D[ナビゲーションIDを取得]
    D --> E[CategoryRoute::load]
    E --> F{カテゴリはアクティブ?}
    F -->|No| G[CategoryNotFound例外]
    F -->|Yes| H{カテゴリタイプはLINK?}
    H -->|Yes| I[リダイレクトURL生成]
    I --> J[RedirectResponse返却]
    H -->|No| K[メタ情報設定]
    K --> L[CMSページを設定]
    L --> M[Canonical URL設定]
    M --> N[NavigationPageLoadedEvent発火]
    N --> O[index.html.twigレンダリング]
    O --> P[終了]
    G --> Q[エラーページ表示]
    Q --> P
    J --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-58-01 | ログイン不要 | ログインなしでアクセス可能 | 全リクエスト |
| BR-58-02 | HTTPキャッシュ有効 | HTTPキャッシュ属性が設定される | 全リクエスト |
| BR-58-03 | SEO有効 | SEOフレンドリーURLが適用される | 全リクエスト |
| BR-58-04 | リンクカテゴリリダイレクト | TYPE_LINKカテゴリはリダイレクト | リンクタイプカテゴリ |
| BR-58-05 | アクティブカテゴリのみ | 非アクティブカテゴリはエラー | カテゴリ取得時 |

### 計算ロジック

- Canonical URL: `NavigationPageSeoUrlRoute.ROUTE_NAME` のSEO URL + ページネーションパラメータ

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カテゴリ取得 | category | SELECT | 指定カテゴリを取得 |
| CMSページ取得 | cms_page | SELECT | カテゴリに紐付くCMSページを取得 |
| 翻訳取得 | category_translation | SELECT | 翻訳情報取得 |

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

#### category

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, active, type, cms_page_id, meta_title, meta_description, keywords, link_type, internal_link, external_link | id = ? | カテゴリ取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| CATEGORY_NOT_FOUND | カテゴリ未存在 | カテゴリが存在しないまたは非アクティブ | 正しいURLを使用 |
| CATEGORY_NOT_FOUND | リンク先未設定 | リンクタイプカテゴリでリンク先がない | 管理画面でリンク先を設定 |

### リトライ仕様

特になし

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

- 読み取り専用のため、トランザクション管理は不要
- SELECTクエリのみ実行

## パフォーマンス要件

- レスポンス時間: 500ms以内（キャッシュヒット時は100ms以内）
- HTTPキャッシュ有効

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

- HTTPキャッシュが有効のため、ユーザー固有情報は含めない
- リダイレクト時はSeoUrlPlaceholderHandlerでURL検証

## 備考

- カテゴリタイプ: page（通常）、folder（フォルダ）、link（リンク）
- リンクタイプカテゴリ: internal（内部リンク）、external（外部リンク）
- CMSページの商品一覧ブロックでカテゴリ商品を表示

---

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

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

### 推奨読解順序

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

まず、カテゴリエンティティのデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | CategoryDefinition.php | `src/Core/Content/Category/CategoryDefinition.php` | カテゴリエンティティ定義。type、cmsPageId、linkType、internalLink、externalLinkフィールドを確認 |

**読解のコツ**: カテゴリのtypeフィールド（page/folder/link）でページ表示とリダイレクトを分岐する。

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

処理の起点となるコントローラーを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | NavigationController.php | `src/Storefront/Controller/NavigationController.php` | Storefront側のエントリーポイント。indexメソッドがカテゴリページ表示の入口 |

**主要処理フロー**:
1. **65-71行目**: indexメソッドのルート定義。GET `/navigation/{navigationId}`、SEO有効、HTTPキャッシュ有効
2. **72-93行目**: indexメソッドの実装
3. **74-76行目**: NavigationPageLoaderでページをロード、フック発火
4. **78-90行目**: TYPE_LINKカテゴリの場合はリダイレクト処理
5. **92行目**: 通常カテゴリはindex.html.twigでレンダリング

#### Step 3: リダイレクト処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NavigationController.php | `src/Storefront/Controller/NavigationController.php` | リンクタイプカテゴリのリダイレクト処理 |

**主要処理フロー**:
- **81行目**: カテゴリタイプがTYPE_LINKかチェック
- **82行目**: ホストURLを取得
- **83行目**: categoryUrlGeneratorでリダイレクト先URLを生成
- **85-87行目**: URLが生成できない場合はCategoryNotFound例外
- **89行目**: seoUrlReplacerでプレースホルダーを実際のURLに置換
- **90行目**: RedirectResponseを返却

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | NavigationPageLoader.php | `src/Storefront/Page/Navigation/NavigationPageLoader.php` | ページ生成のロジック（ホームページ表示と共通） |

**主要処理フロー**:
- **34-74行目**: loadメソッド（No.57ホームページ表示と共通）
- **39行目**: ナビゲーションIDはリクエストから取得（ホームページと異なる点）
- **57-60行目**: Canonical URLはNavigationPageSeoUrlRouteを使用

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

```
NavigationController::index (Storefront)
    │
    ├─ NavigationPageLoader::load
    │      ├─ GenericPageLoader::load
    │      ├─ CategoryRoute::load
    │      ├─ loadMetaData
    │      └─ SeoUrlReplacer::generate
    │
    └─ [TYPE_LINKの場合]
           ├─ CategoryUrlGenerator::generate
           ├─ SeoUrlPlaceholderHandler::replace
           └─ RedirectResponse
```

### データフロー図

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

navigationId (UUID) ────▶ NavigationController::index
                              │
Request ───────────────────▶ │
                              │
SalesChannelContext ───────▶ │
                              ▼
                         NavigationPageLoader::load
                              │
                              ├─▶ category テーブル (SELECT)
                              │
                              └─▶ cms_page テーブル (SELECT)
                                          │
                                          ▼
                              ┌─────────────────────────────┐
                              │ TYPE_LINK?                  │
                              │  Yes: RedirectResponse      │
                              │  No:  Twigテンプレート      │
                              └─────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| NavigationController.php | `src/Storefront/Controller/NavigationController.php` | ソース | Storefrontコントローラー、エントリーポイント |
| NavigationPageLoader.php | `src/Storefront/Page/Navigation/NavigationPageLoader.php` | ソース | ページローダー、ビジネスロジック |
| NavigationPage.php | `src/Storefront/Page/Navigation/NavigationPage.php` | ソース | ページクラス |
| CategoryRoute.php | `src/Core/Content/Category/SalesChannel/CategoryRoute.php` | ソース | カテゴリ取得ルート |
| CategoryUrlGenerator.php | `src/Core/Content/Category/Service/CategoryUrlGenerator.php` | ソース | リンクカテゴリURL生成 |
| NavigationPageSeoUrlRoute.php | `src/Storefront/Framework/Seo/SeoUrlRoute/NavigationPageSeoUrlRoute.php` | ソース | SEO URLルート定義 |
| index.html.twig | `src/Storefront/Resources/views/storefront/page/content/index.html.twig` | テンプレート | コンテンツページテンプレート |
