# 画面設計書 5-検索結果ページ

## 概要

本ドキュメントは、Shopware 6 Storefrontにおける検索結果ページの画面設計書である。キーワードによる商品検索結果を一覧表示し、フィルタリングやソート機能を提供する画面である。

### 本画面の処理概要

検索結果ページは、顧客が入力した検索キーワードに基づいて商品を検索し、結果を一覧表示する画面である。フィルターパネルによる絞り込み、ソート順の変更、ページネーションに対応している。

**業務上の目的・背景**：検索機能はECサイトにおける主要なナビゲーション手段の一つである。顧客が特定の商品名、ブランド、特徴などで直接検索することで、カテゴリナビゲーションを経由せずに目的の商品にたどり着ける。検索精度の向上と適切なフィルタリングオプションの提供により、顧客の商品発見を支援し、コンバージョンを促進する。

**画面へのアクセス方法**：ヘッダーの検索バーにキーワードを入力してEnterキーまたは検索ボタンを押下、検索サジェストの「すべての結果を見る」リンクをクリック、URL直接アクセス（`/search?search=キーワード`）でアクセスする。

**主要な操作・処理内容**：
1. 検索キーワードによる商品検索（SearchPageLoader）
2. 検索結果の一覧表示
3. フィルターパネルによる絞り込み
4. ソート順の変更
5. ページネーションによるページ切り替え
6. 商品詳細ページへの遷移

**画面遷移**：
- 遷移元：ヘッダー検索バー、検索サジェスト
- 遷移先：商品詳細ページ、カートページ（商品追加時）

**権限による表示制御**：全ユーザーがアクセス可能。特別な権限制御なし。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 32 | 商品検索 | 主機能 | キーワードによる商品検索（SearchController::search） |
| 34 | 検索フィルター | 補助機能 | 検索結果のフィルタリング（SearchController::filter） |

## 画面種別

一覧 / 検索結果

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| URL | `/search` |
| ルート名 | `frontend.search.page` |
| HTTPメソッド | GET |
| HTTPキャッシュ | 無効（検索クエリによる動的ページ） |

## 入出力項目

### 入力項目

| 項目名 | パラメータ名 | 必須 | 型 | 説明 |
|--------|-------------|------|-----|------|
| 検索キーワード | search | 必須 | string | 検索文字列 |
| ソート順 | order | 任意 | string | 並び順（例: name-asc, price-desc） |
| ページ番号 | p | 任意 | int | ページネーション用 |
| フィルター | properties, manufacturer, price など | 任意 | mixed | 商品フィルター条件 |

### 出力データ

| 項目名 | データソース | 説明 |
|--------|-------------|------|
| page | SearchPageLoader | ページ全体のデータオブジェクト |
| page.searchTerm | リクエスト | 検索キーワード |
| page.listing | ProductListingResult | 検索結果の商品一覧 |
| page.listing.total | 検索結果 | 総ヒット件数 |

## 表示項目

| セクション | 表示項目 | 説明 |
|-----------|---------|------|
| ヘッダー | 共通ヘッダー | ロゴ、ナビゲーション、検索、カート |
| 見出し | 検索結果見出し | 「{件数}件の結果: {検索キーワード}」形式 |
| サイドバー | フィルターパネル | 価格範囲、メーカー、プロパティ等 |
| メイン | ソート選択 | 並び順のドロップダウン |
| メイン | 商品一覧 | 商品カードのグリッド/リスト表示 |
| メイン | ページネーション | ページ切り替えナビゲーション |
| フッター | 共通フッター | フッターリンク |

## イベント仕様

### 1-検索実行

検索フォームを送信すると、SearchController::searchメソッドが実行される。

**処理フロー**：
1. SearchPageLoaderがリクエストとSalesChannelContextを受け取る
2. 検索キーワードが空の場合、ホームページへリダイレクト
3. ProductSearchRouteで商品を検索
4. 検索結果が1件で商品番号と完全一致の場合、商品詳細へリダイレクト
5. SearchPageLoadedHookを発火
6. Twigテンプレートでレンダリング

### 2-ファーストヒット処理

検索結果が1件で、検索キーワードが商品番号と完全一致する場合、商品詳細ページへ自動リダイレクトされる。

```php
if ($page->getListing()->getTotal() === 1) {
    if ($request->query->get('search') === mb_strtolower($product->getProductNumber())) {
        return $this->redirectToRoute('frontend.detail.page', ['productId' => $product->getId()]);
    }
}
```

### 3-フィルター適用（AJAX）

フィルターを変更すると、AJAX経由で商品一覧が更新される。

**エンドポイント**: `/widgets/search`
**ルート名**: `widgets.search.pagelet.v2`

### 4-フィルター集計取得

フィルターの選択可能な値を取得する。

**エンドポイント**: `/widgets/search/filter`
**ルート名**: `widgets.search.filter`

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 検索実行 | product（Elasticsearch/OpenSearch経由） | SELECT | 商品検索 |

本画面は表示専用であり、データベースへの書き込み操作は発生しない。

## メッセージ仕様

| メッセージ種別 | 条件 | メッセージ内容 |
|--------------|------|--------------|
| 情報 | 検索結果0件 | 「検索結果がありません」 |

## 例外処理

| 例外条件 | 対応 |
|---------|------|
| 検索キーワード未指定 | ホームページへリダイレクト |
| RoutingException（MISSING_REQUEST_PARAMETER） | ホームページへフォワード |

## 備考

- 検索はElasticsearch/OpenSearchを使用（設定による）
- 検索結果のヒット件数はpage.listing.totalで取得可能
- フィルターの集計値はaggregationsとして返却される
- 商品番号完全一致の場合のファーストヒット機能は利便性向上のため

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SearchPage.php | `src/Storefront/Page/Search/SearchPage.php` | ページオブジェクトの構造、searchTerm/listingプロパティ |

**読解のコツ**: SearchPageはProductListingResultをlistingプロパティとして持ち、検索結果の商品一覧とアグリゲーション（集計）情報を含む。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SearchController.php | `src/Storefront/Controller/SearchController.php` | searchメソッド（43-69行目） |

**主要処理フロー**:
1. **48-51行目**: SearchPageLoaderで検索実行
2. **53-57行目**: ファーストヒット処理（商品番号一致時のリダイレクト）
3. **58-64行目**: RoutingException時のホームページフォワード
4. **66行目**: SearchPageLoadedHookでフック処理
5. **68行目**: テンプレートレンダリング

#### Step 3: AJAX更新を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SearchController.php | `src/Storefront/Controller/SearchController.php` | ajaxメソッド（93-111行目） |
| 3-2 | SearchController.php | `src/Storefront/Controller/SearchController.php` | filterメソッド（116-152行目） |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.html.twig | `src/Storefront/Resources/views/storefront/page/search/index.html.twig` | メインテンプレート |
| 4-2 | search-pagelet.html.twig | `src/Storefront/Resources/views/storefront/page/search/search-pagelet.html.twig` | AJAX更新用 |

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

```
SearchController::search()
    |
    +-- SearchPageLoader::load()
    |       |
    |       +-- ProductSearchRoute::load()
    |               +-- Elasticsearch/OpenSearch
    |
    +-- handleFirstHit() [条件付き]
    |       +-- redirectToRoute('frontend.detail.page')
    |
    +-- SearchPageLoadedHook
    |
    +-- renderStorefront()
            +-- index.html.twig
                    +-- search-pagelet.html.twig
```

### データフロー図

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

search=キーワード  -->  SearchController::search()
    |                       |
    v                       v
sort, filter          SearchPageLoader
parameters                 |
                           v
                   ProductSearchRoute
                           |
                           +-- Elasticsearch/OpenSearch
                           |
                           v
                   SearchPage              -->  HTML Response
                           |
                           v
                   index.html.twig
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SearchController.php | `src/Storefront/Controller/SearchController.php` | コントローラー | ルーティングエントリーポイント |
| SearchPageLoader.php | `src/Storefront/Page/Search/SearchPageLoader.php` | ページローダー | ページデータの構築 |
| SearchPage.php | `src/Storefront/Page/Search/SearchPage.php` | データ構造 | ページオブジェクト定義 |
| index.html.twig | `src/Storefront/Resources/views/storefront/page/search/index.html.twig` | テンプレート | メインテンプレート |
| search-pagelet.html.twig | `src/Storefront/Resources/views/storefront/page/search/search-pagelet.html.twig` | テンプレート | AJAX用テンプレート |
| listing.html.twig | `src/Storefront/Resources/views/storefront/component/product/listing.html.twig` | テンプレート | 商品一覧表示 |
| filter-panel.html.twig | `src/Storefront/Resources/views/storefront/component/listing/filter-panel.html.twig` | テンプレート | フィルターパネル |
