# 機能設計書 56-ニュースレター設定

## 概要

本ドキュメントは、Shopware Storefrontにおける「ニュースレター設定」機能の設計仕様を定義する。ログイン済み顧客がマイアカウントページからニュースレターの購読設定を変更する機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：ECサイトにおいて、顧客がニュースレターの購読状態を自己管理できることは、顧客満足度とGDPRコンプライアンスの観点から重要である。本機能により、ログイン済み顧客はマイアカウントページからニュースレターの購読開始・解除をいつでも変更できる。

**機能の利用シーン**：顧客がマイアカウントページのニュースレター設定セクションで、購読チェックボックスを変更して送信した際に本機能が呼び出される。購読開始の場合はダブルオプトイン設定に応じて確認メールが送信される場合がある。購読解除の場合は即座にステータスが更新される。

**主要な処理内容**：
1. ログイン状態の確認
2. リクエストから購読オプションを取得（subscribe/unsubscribe）
3. 顧客情報からニュースレター登録データを構築
4. 購読開始または購読解除ルートを呼び出し
5. 処理結果に応じたメッセージを設定
6. 更新されたニュースレター設定ページレットを返却

**関連システム・外部連携**：本機能は Store API の `NewsletterSubscribeRoute` または `NewsletterUnsubscribeRoute` を内部的に利用する。ダブルオプトイン設定時は確認メールが送信される。

**権限による制御**：本機能はログイン必須（`LOGIN_REQUIRED`属性）であり、未認証ユーザーはアクセスできない。顧客は自分のニュースレター設定のみ変更可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | プロフィールページ | 主画面 | ニュースレター設定の変更フォーム |
| 40 | ニュースレター受信者管理（管理画面） | 参照画面 | 購読者リストの確認 |

## 機能種別

CRUD操作（Update）/ Ajax API / 設定変更

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| option | string | Yes | 購読オプション（direct/subscribe/unsubscribe） | 有効なオプション値 |
| storefrontUrl | string | No | 確認メールのリンク用URL | 自動設定 |

### 入力データソース

- リクエストボディ: RequestDataBag形式
- 顧客情報: CustomerEntityから自動取得（email、salutationId、firstName、lastName、住所情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| newsletterAccountPagelet | NewsletterAccountPagelet | ニュースレター設定ページレット |

### 出力先

- HTTPレスポンス: TwigテンプレートによるページレットHTML

## 処理フロー

### 処理シーケンス

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

2. NewsletterAccountPageletLoader::action 実行
   └─ ベースページレット作成
   └─ オプションに応じて購読/解除を判定

3. 購読処理（subscribe/direct オプションの場合）
   └─ 顧客情報からデータバッグを構築
   └─ NewsletterSubscribeRoute::subscribe を呼び出し
   └─ 成功/失敗メッセージを設定

4. 購読解除処理（unsubscribe オプションの場合）
   └─ 顧客情報からデータバッグを構築
   └─ NewsletterUnsubscribeRoute::unsubscribe を呼び出し
   └─ 成功/失敗メッセージを設定

5. ステータス取得
   └─ AccountNewsletterRecipientRoute でステータスを取得
   └─ ページレットにステータスを設定

6. 追加メッセージ設定
   └─ STATUS_NOT_SET の場合は「確認メール送信済み」メッセージ

7. レスポンス返却
   └─ ページレットをレンダリングして返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始: subscribeCustomer呼び出し] --> B[NewsletterAccountPageletLoader::action]
    B --> C{オプションは購読系?}
    C -->|Yes: subscribe/direct| D[subscribe処理]
    C -->|No: unsubscribe| E[unsubscribe処理]
    D --> F{購読成功?}
    F -->|Yes| G{DOI設定?}
    G -->|Yes| H[確認メール送信メッセージ]
    G -->|No| I[登録完了メッセージ]
    F -->|No| J[エラーメッセージ]
    E --> K{解除成功?}
    K -->|Yes| L[解除完了メッセージ]
    K -->|No| M[エラーメッセージ]
    H --> N[ステータス取得]
    I --> N
    J --> N
    L --> N
    M --> N
    N --> O{ステータスはNOT_SET?}
    O -->|Yes| P[確認待ちメッセージ追加]
    O -->|No| Q[ページレット返却]
    P --> Q
    Q --> R[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-56-01 | ログイン必須 | ログイン済み顧客のみが利用可能 | 全リクエスト |
| BR-56-02 | 自己設定のみ | 自分のニュースレター設定のみ変更可能 | 全リクエスト |
| BR-56-03 | ダブルオプトイン | システム設定に応じて確認メール送信 | 購読開始時 |
| BR-56-04 | 即時解除 | 購読解除は即座に反映 | 購読解除時 |
| BR-56-05 | 顧客情報自動入力 | email、氏名、住所は顧客情報から自動取得 | 購読処理時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 受信者取得/更新 | newsletter_recipient | SELECT/INSERT/UPDATE | 購読状態の取得・更新 |
| 顧客情報取得 | customer | SELECT | 顧客の基本情報取得 |

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

#### newsletter_recipient

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | status | email = ? AND sales_channel_id = ? | ステータス取得 |
| INSERT/UPDATE | email, status, salutation_id, first_name, last_name, etc. | 購読情報 | 購読開始時 |
| UPDATE | status | 'optOut' | 購読解除時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 認証エラー | 未ログイン状態でアクセス | ログインが必要 |
| NEWSLETTER_SUBSCRIBE_FAILED | 購読失敗 | 購読処理でエラー発生 | エラーメッセージ表示 |
| NEWSLETTER_UNSUBSCRIBE_FAILED | 解除失敗 | 解除処理でエラー発生 | エラーメッセージ表示 |

### リトライ仕様

特になし

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

- `NewsletterSubscribeRoute` / `NewsletterUnsubscribeRoute` 内でトランザクションが管理される
- 失敗時は自動ロールバック

## パフォーマンス要件

- レスポンス時間: 500ms以内
- Ajax通信のため、非同期処理で画面遷移なし

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

- ログイン必須（`LOGIN_REQUIRED`属性による認証チェック）
- CSRF対策（Symfonyフレームワークによる自動保護）
- 顧客情報はセッションから取得（入力改ざん防止）

## 備考

- ダブルオプトイン設定は `core.newsletter.doubleOptInRegistered` で管理
- 購読状態: undefined / notSet / optIn / optOut
- XmlHttpRequestで非同期更新、ページ全体のリロード不要

---

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

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

### 推奨読解順序

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

まず、ニュースレター設定ページレットのデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | NewsletterAccountPagelet.php | `src/Storefront/Pagelet/Newsletter/Account/NewsletterAccountPagelet.php` | ページレット構造。customer、newsletterStatus、newsletterDoi、messages、successプロパティを確認 |

**読解のコツ**: PageletはPage内で部分的に更新可能なコンポーネント。Ajax通信で部分更新に使用される。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | NewsletterController.php | `src/Storefront/Controller/NewsletterController.php` | Storefront側のエントリーポイント。subscribeCustomerメソッドが設定変更の入口 |

**主要処理フロー**:
1. **72-79行目**: subscribeCustomerメソッドのルート定義。POST `/widgets/account/newsletter`
2. **80-88行目**: subscribeCustomerメソッドの実装
3. **81-83行目**: NewsletterAccountPageletLoaderのactionメソッドを呼び出し
4. **85-87行目**: Twigテンプレートでページレットをレンダリング

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

実際のビジネスロジックはページレットローダーに実装されている。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NewsletterAccountPageletLoader.php | `src/Storefront/Pagelet/Newsletter/Account/NewsletterAccountPageletLoader.php` | 設定変更の核心ロジック |

**主要処理フロー**:
- **67-107行目**: actionメソッドの実装
- **73-74行目**: オプション判定（subscribe/direct/unsubscribe）
- **76-78行目**: unsubscribeの場合はオプションを設定
- **80行目**: storefrontUrlを設定
- **82行目**: ベースページレット作成
- **84-88行目**: 購読系オプションの場合はsubscribeメソッドを呼び出し
- **86-88行目**: それ以外はunsubscribeメソッドを呼び出し
- **90-91行目**: ステータスを取得して設定
- **93-103行目**: STATUS_NOT_SETの場合は確認待ちメッセージを追加

#### Step 4: 購読/解除処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | NewsletterAccountPageletLoader.php | `src/Storefront/Pagelet/Newsletter/Account/NewsletterAccountPageletLoader.php` | subscribe、unsubscribeメソッド |

**主要処理フロー**:
- **109-145行目**: subscribeメソッド
  - **111-116行目**: newsletterSubscribeRouteを呼び出し
  - **118-131行目**: 成功時はDOI設定に応じたメッセージを設定
  - **132-141行目**: 例外時はエラーメッセージを設定
- **147-177行目**: unsubscribeメソッド
  - **149-153行目**: newsletterUnsubscribeRouteを呼び出し
  - **155-163行目**: 成功時は解除完了メッセージを設定
  - **164-173行目**: 例外時はエラーメッセージを設定
- **212-233行目**: hydrateFromCustomerメソッド。顧客情報からDataBagを構築

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

```
NewsletterController::subscribeCustomer (Storefront)
    │
    └─ NewsletterAccountPageletLoader::action
           │
           ├─ getBasePagelet (ページレット初期化)
           │      └─ SystemConfigService::get (DOI設定取得)
           │
           ├─ subscribe (購読処理)
           │      ├─ hydrateFromCustomer (顧客情報抽出)
           │      └─ NewsletterSubscribeRoute::subscribe
           │
           ├─ unsubscribe (解除処理)
           │      ├─ hydrateFromCustomer (顧客情報抽出)
           │      └─ NewsletterUnsubscribeRoute::unsubscribe
           │
           └─ getNewsletterRecipientStatus (ステータス取得)
                  └─ AccountNewsletterRecipientRoute::load
```

### データフロー図

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

option (subscribe等) ───▶ NewsletterController::subscribeCustomer
                              │
RequestDataBag ────────────▶ │
                              │
SalesChannelContext ───────▶ │
                              │
CustomerEntity ────────────▶ │
                              ▼
                         NewsletterAccountPageletLoader::action
                              │
                              ├─▶ newsletter_recipient テーブル (SELECT/INSERT/UPDATE)
                              │
                              └─▶ customer テーブル (SELECT - 顧客情報取得)
                                          │
                                          ▼
                                   Twigテンプレート ──────▶ ページレットHTML
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| NewsletterController.php | `src/Storefront/Controller/NewsletterController.php` | ソース | Storefrontコントローラー、エントリーポイント |
| NewsletterAccountPageletLoader.php | `src/Storefront/Pagelet/Newsletter/Account/NewsletterAccountPageletLoader.php` | ソース | ページレットローダー、ビジネスロジック |
| NewsletterAccountPagelet.php | `src/Storefront/Pagelet/Newsletter/Account/NewsletterAccountPagelet.php` | ソース | ページレットクラス |
| NewsletterSubscribeRoute.php | `src/Core/Content/Newsletter/SalesChannel/NewsletterSubscribeRoute.php` | ソース | 購読登録ルート |
| NewsletterUnsubscribeRoute.php | `src/Core/Content/Newsletter/SalesChannel/NewsletterUnsubscribeRoute.php` | ソース | 購読解除ルート |
| AccountNewsletterRecipientRoute.php | `src/Core/Checkout/Customer/SalesChannel/AccountNewsletterRecipientRoute.php` | ソース | 受信者ステータス取得ルート |
| newsletter.html.twig | `src/Storefront/Resources/views/storefront/page/account/newsletter.html.twig` | テンプレート | ニュースレター設定テンプレート |
