# 機能設計書 16-住所一覧表示

## 概要

本ドキュメントは、Shopwareストアフロントにおける「住所一覧表示」機能の設計内容を記載する。顧客が登録した配送先・請求先住所の一覧を表示し、住所の管理（新規作成、編集、削除、デフォルト設定）へのアクセスを提供する住所帳ページである。

### 本機能の処理概要

**業務上の目的・背景**：ECサイトにおいて、顧客が複数の配送先住所を管理できることは重要である。自宅以外への配送（職場、贈り物等）や、請求先と配送先を分ける法人利用など、多様なニーズに対応する。本機能は、顧客が登録した住所を一覧表示し、住所管理機能への導線を提供する。

**機能の利用シーン**：
- 顧客が登録済みの住所を確認したい場合
- 新しい住所を追加したい場合（新規作成画面への遷移）
- 既存の住所を編集・削除したい場合
- デフォルトの配送先・請求先を変更したい場合

**主要な処理内容**：
1. ログイン認証の確認
2. 顧客の登録住所一覧の取得
3. 国・州マスターデータの取得
4. 敬称マスターデータの取得
5. カート情報の取得（住所変更の影響確認用）
6. ページオブジェクトの構築とテンプレートレンダリング

**関連システム・外部連携**：Store API（AbstractListAddressRoute、AbstractCountryRoute、AbstractSalutationRoute）を経由してデータを取得。

**権限による制御**：ログイン必須（`LOGIN_REQUIRED`属性）。ゲストユーザーは住所帳にアクセス不可（カートエラー経由でのみ住所編集可能）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 18 | 住所帳ページ | 主画面 | 登録住所一覧の表示 |
| 19 | 住所作成ページ | 遷移先画面 | 新規住所登録 |
| 20 | 住所編集ページ | 遷移先画面 | 既存住所の編集 |

## 機能種別

データ参照 / ページ表示

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| request | Request | Yes | HTTPリクエストオブジェクト | なし |
| context | SalesChannelContext | Yes | 販売チャネルコンテキスト | ログイン顧客情報を含む |
| customer | CustomerEntity | Yes | ログイン中の顧客エンティティ | セッションから自動取得 |
| addressId | string | No | 特定の住所を選択表示する場合のID | 有効なUUID形式 |

### 入力データソース

- セッション情報（ログイン顧客情報）
- クエリパラメータ（addressId）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| page | AddressListingPage | ページオブジェクト |
| page.addresses | CustomerAddressCollection | 顧客の住所一覧（firstName昇順） |
| page.countries | CountryCollection | 国マスターデータ（position, name順） |
| page.salutations | SalutationCollection | 敬称マスターデータ |
| page.cart | Cart | カート情報 |
| page.address | CustomerAddressEntity\|null | 選択された住所（addressIdが指定された場合） |

### 出力先

- 画面表示（Twigテンプレート `@Storefront/storefront/page/account/addressbook/index.html.twig`）

## 処理フロー

### 処理シーケンス

```
1. ルーティング処理
   └─ /account/address パスへのGETリクエストを受信、LOGIN_REQUIRED属性により認証確認

2. コントローラー処理 (AddressController::accountAddressOverview)
   └─ AddressListingPageLoaderを呼び出してページオブジェクトを構築

3. ページローダー処理 (AddressListingPageLoader::load)
   ├─ GenericPageLoaderで基本ページ情報を取得
   ├─ 敬称マスターを取得してソート
   ├─ 国・州マスターを取得してソート
   ├─ 住所一覧を取得（firstName昇順）
   ├─ カート情報を取得
   ├─ addressIdが指定されていれば該当住所をセット
   └─ イベントをディスパッチ

4. レスポンス生成
   └─ Twigテンプレートをレンダリングして返却
```

### フローチャート

```mermaid
flowchart TD
    A[GET /account/address] --> B{ログイン済み?}
    B -->|No| C[ログイン画面へリダイレクト]
    B -->|Yes| D[AddressListingPageLoader::load]
    D --> E[GenericPageLoader::load]
    E --> F[getSalutations - 敬称取得]
    F --> G[getCountries - 国・州取得]
    G --> H[listAddressRoute - 住所一覧取得]
    H --> I[cartService - カート取得]
    I --> J{addressId指定あり?}
    J -->|Yes| K[該当住所をpageにセット]
    J -->|No| L[addressはnull]
    K --> M[AddressListingPageLoadedEvent発火]
    L --> M
    M --> N[AddressListingPageLoadedHook実行]
    N --> O[Twigテンプレートレンダリング]
    O --> P[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-16-01 | ログイン必須 | 未ログインユーザーはアクセス不可 | 全リクエスト |
| BR-16-02 | キャッシュ無効 | NO_STORE属性によりキャッシュ対象外 | 全リクエスト |
| BR-16-03 | SEO無効 | seo=falseによりSEO対象外 | 全リクエスト |
| BR-16-04 | 住所ソート | firstName昇順でソート | 全リクエスト |
| BR-16-05 | 国ソート | position昇順、name昇順でソート | 全リクエスト |
| BR-16-06 | 州ソート | position昇順、name昇順でソート | 国に紐づく州 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 住所一覧取得 | customer_address | SELECT | 顧客の住所一覧取得 |
| 国マスター取得 | country | SELECT | 国一覧取得 |
| 州マスター取得 | country_state | SELECT | 州一覧取得 |
| 敬称マスター取得 | salutation | SELECT | 敬称一覧取得 |

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

#### customer_address

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全フィールド | customer_id = ログイン顧客ID、firstName昇順 | - |

#### country

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 全フィールド | position昇順、name昇順 | statesアソシエーション含む |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | CustomerNotLoggedInException | 未ログイン状態でアクセス | ログイン画面へリダイレクト |
| - | RoutingException | ルーティングエラー | エラーページ表示 |
| - | CategoryNotFoundException | カテゴリ情報取得失敗 | エラーページ表示 |

### リトライ仕様

特になし（読み取り専用処理）

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

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

## パフォーマンス要件

- 住所一覧はfirstNameでソートして取得
- 国・州マスターはpositionとnameでソートして取得
- 必要なアソシエーションのみ明示的に指定

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

- LOGIN_REQUIRED属性によりログイン必須を強制
- NO_STORE属性によりブラウザキャッシュ無効化（住所情報保護）
- 顧客は自分の住所のみ参照可能（customer_idでフィルタ）

## 備考

- ゲストユーザーは住所帳にアクセスできない（カートエラー経由で住所編集のみ可能）
- addressIdパラメータは住所編集画面からの戻り時等に使用される

---

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

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

### 推奨読解順序

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

ページオブジェクトの構造を理解することが最初のステップである。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AddressListingPage.php | `src/Storefront/Page/Address/Listing/AddressListingPage.php` | ページオブジェクトのプロパティ（addresses, countries, salutations, cart, address）を確認 |
| 1-2 | CustomerAddressEntity.php | `src/Core/Checkout/Customer/Aggregate/CustomerAddress/CustomerAddressEntity.php` | 住所エンティティの構造を確認 |

**読解のコツ**: AddressListingPageはPageを継承し、住所管理に必要なマスターデータも保持する。

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

コントローラーがリクエストを受け取り、ページローダーに処理を委譲する流れを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AddressController.php | `src/Storefront/Controller/AddressController.php` | accountAddressOverviewメソッド（70-87行目）がエントリーポイント |

**主要処理フロー**:
1. **82行目**: `$this->addressListingPageLoader->load($request, $context, $customer)` でページローダーを呼び出し
2. **84行目**: `$this->hook(new AddressListingPageLoadedHook($page, $context))` でフック実行
3. **86行目**: Twigテンプレートをレンダリングして返却

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

データ取得とページ構築のロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AddressListingPageLoader.php | `src/Storefront/Page/Address/Listing/AddressListingPageLoader.php` | loadメソッド（50-76行目）の処理フローを確認 |

**主要処理フロー**:
- **52行目**: GenericPageLoaderで基本ページ情報取得
- **54-55行目**: AddressListingPageに変換、メタ情報設定
- **57行目**: 敬称マスターを取得
- **59行目**: 国・州マスターを取得
- **61行目**: 住所一覧を取得（firstName昇順）
- **65行目**: カート情報を取得
- **67-69行目**: addressIdが指定されていれば該当住所をセット
- **71-73行目**: ページロードイベントをディスパッチ

#### Step 4: Store APIルートを理解する

データ取得に使用されるStore APIルートを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AbstractListAddressRoute.php | `src/Core/Checkout/Customer/SalesChannel/AbstractListAddressRoute.php` | 住所一覧取得APIの抽象定義 |
| 4-2 | AbstractCountryRoute.php | `src/Core/System/Country/SalesChannel/AbstractCountryRoute.php` | 国マスター取得API |
| 4-3 | AbstractSalutationRoute.php | `src/Core/System/Salutation/SalesChannel/AbstractSalutationRoute.php` | 敬称マスター取得API |

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

```
AddressController::accountAddressOverview
    │
    ├─ AddressListingPageLoader::load
    │      ├─ GenericPageLoaderInterface::load
    │      ├─ getSalutations (private)
    │      │      └─ AbstractSalutationRoute::load
    │      ├─ getCountries (private)
    │      │      └─ AbstractCountryRoute::load
    │      ├─ AbstractListAddressRoute::load
    │      ├─ CartService::getCart
    │      └─ EventDispatcher::dispatch
    │
    └─ AddressListingPageLoadedHook (フック実行)
```

### データフロー図

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

Request (GET /account/address)
        │
        ▼
SalesChannelContext ───▶ AddressController::accountAddressOverview
        │                         │
        ▼                         ▼
CustomerEntity ─────────▶ AddressListingPageLoader::load
                                  │
                    ┌─────────────┼─────────────┐
                    ▼             ▼             ▼
           SalutationRoute  CountryRoute  ListAddressRoute
                    │             │             │
                    └─────────────┴─────────────┘
                                  │
                                  ▼
                           CartService
                                  │
                                  ▼
                         AddressListingPage
                                  │
                                  ▼
                         Twigテンプレート
                                  │
                                  ▼
                         HTMLレスポンス
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AddressController.php | `src/Storefront/Controller/AddressController.php` | コントローラー | HTTPリクエスト処理のエントリーポイント |
| AddressListingPageLoader.php | `src/Storefront/Page/Address/Listing/AddressListingPageLoader.php` | ページローダー | ページデータの構築ロジック |
| AddressListingPage.php | `src/Storefront/Page/Address/Listing/AddressListingPage.php` | ページモデル | ページデータを保持するオブジェクト |
| AddressListingPageLoadedEvent.php | `src/Storefront/Page/Address/Listing/AddressListingPageLoadedEvent.php` | イベント | ページロード完了イベント |
| AddressListingPageLoadedHook.php | `src/Storefront/Page/Address/Listing/AddressListingPageLoadedHook.php` | フック | アプリ/プラグイン用フック |
| index.html.twig | `src/Storefront/Resources/views/storefront/page/account/addressbook/index.html.twig` | テンプレート | 住所一覧画面表示用Twigテンプレート |
| AbstractListAddressRoute.php | `src/Core/Checkout/Customer/SalesChannel/AbstractListAddressRoute.php` | Store API | 住所一覧取得API |
| AbstractCountryRoute.php | `src/Core/System/Country/SalesChannel/AbstractCountryRoute.php` | Store API | 国マスター取得API |
| AbstractSalutationRoute.php | `src/Core/System/Salutation/SalesChannel/AbstractSalutationRoute.php` | Store API | 敬称マスター取得API |
| CustomerAddressEntity.php | `src/Core/Checkout/Customer/Aggregate/CustomerAddress/CustomerAddressEntity.php` | エンティティ | 住所データモデル |
