# 通知設計書 4-PushNotification

## 概要

本ドキュメントは、Symfony NotifierコンポーネントにおけるPush通知（PushNotification）の設計仕様を記述する。PushNotificationInterfaceを実装した通知オブジェクトがPushChannelを通じてプッシュ通知を送信する仕組みについて詳細に定義する。

### 本通知の処理概要

PushNotificationは、Symfony Notifierコンポーネントが提供するプッシュ通知チャネルを通じた通知機能である。PushNotificationInterfaceを実装することで、Firebase Cloud Messaging（FCM）、Apple Push Notification Service（APNs）等のプッシュ通知サービスに対してカスタムメッセージを生成・送信できる。

**業務上の目的・背景**：プッシュ通知はモバイルアプリケーションやWebアプリケーションにおいて、ユーザーがアプリを開いていなくても情報を届けることができる通知手段である。新着メッセージ、注文状況の更新、リアルタイムアラートなど、即時にユーザーの注意を引く必要があるシナリオで使用される。

**通知の送信タイミング**：Notifier::send()が呼び出され、Notificationのchannelsに"push"が含まれている場合、またはChannelPolicyでpushチャネルが選択された場合に送信が実行される。

**通知の受信者**：PushChannelはすべてのRecipientInterfaceをサポートする（supports()が常にtrueを返す）。プッシュ通知の具体的な宛先（デバイストークン等）はMessageOptionsInterfaceを通じて設定される。

**通知内容の概要**：件名（subject）と本文（content）がPushMessageオブジェクトに含まれる。各プッシュ通知サービス固有のオプション（バッジ、サウンド、カスタムデータ等）はMessageOptionsInterfaceで指定可能。

**期待されるアクション**：受信者はプッシュ通知をタップしてアプリを開き、通知内容に応じた操作（メッセージの確認、注文状況の確認など）を行うことが期待される。

## 通知種別

プッシュ通知（Firebase / APNs / OneSignal 等）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（Transport直接送信）または非同期（Messenger Bus経由） |
| 優先度 | Notificationのimportanceに依存 |
| リトライ | Notifierコンポーネント自体にはリトライ機構なし。Messenger利用時はMessengerのリトライ機構に依存 |

### 送信先決定ロジック

1. PushChannel::supports()は常にtrueを返すため、すべてのRecipientに対して利用可能（PushChannel.php 行45-47）
2. 通知オブジェクトがPushNotificationInterfaceを実装している場合、asPushMessage()メソッドでカスタムPushMessageを生成（PushChannel.php 行27-29）
3. カスタムメッセージがnullの場合、PushMessage::fromNotification()によりデフォルトのPushMessageを生成（PushChannel.php 行31）
4. 具体的な送信先（デバイストークン等）はPushMessage::getOptions()内のMessageOptionsInterfaceで指定

## 通知テンプレート

### 本文テンプレート

```
件名: {Notification::getSubject()}
本文: {Notification::getContent()}
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| 該当なし | - | - | PushMessageの基本構造には添付ファイル機能なし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| subject | 通知タイトル | Notification::getSubject() | Yes |
| content | 通知本文 | Notification::getContent() | Yes |
| options | プッシュサービス固有オプション | PushMessage::getOptions() | No |
| recipientId | 宛先識別子 | MessageOptionsInterface::getRecipientId() | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | Notifier::send()の実行 | Notificationのchannelsに"push"が含まれる、またはChannelPolicyでpushが選択される | アプリケーションコードから直接呼び出し |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| なし | PushChannel::supports()は常にtrueを返すため、Recipient側の制限はない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Notifier::send呼び出し] --> B[getChannels: チャネル決定]
    B --> C[PushChannel::supports チェック]
    C -->|常にtrue| D[PushChannel::notify 実行]
    D --> E{PushNotificationInterface?}
    E -->|Yes| F[asPushMessage でカスタムメッセージ生成]
    E -->|No| G[PushMessage::fromNotification でデフォルト生成]
    F --> H[Transport名設定]
    G --> H
    H --> I{Bus設定あり?}
    I -->|Yes| J[Messenger Bus経由で非同期送信]
    I -->|No| K[Transport直接送信]
    J --> L[終了]
    K --> L
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| 該当なし | - | Notifierコンポーネント自体はデータベースを直接参照しない |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| 該当なし | - | Notifierコンポーネント自体はデータベースを直接更新しない |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| LogicException | TransportもBusも両方nullの場合（AbstractChannel.php 行28-30） | PushChannelのコンストラクタでTransportまたはBusの少なくとも一方を指定する |
| TransportExceptionInterface | プッシュ通知サービスへの送信時のエラー | アプリケーション側でcatchして適切に処理する |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Notifierコンポーネント自体にはリトライ機構なし |
| リトライ間隔 | Messenger利用時はMessengerのリトライ設定に依存 |
| リトライ対象エラー | Messenger利用時はMessengerのリトライポリシーに依存 |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | プッシュ通知サービスプロバイダのAPI制限に依存 |
| 1日あたり上限 | 同上 |

### 配信時間帯

Notifierコンポーネント自体に配信時間帯の制限機能はない。

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

- プッシュ通知サービスのAPIキー/認証情報は環境変数で管理すること
- デバイストークンは個人を特定する情報となりうるため、適切に保護すること
- プッシュ通知の内容にはロック画面に表示される可能性があるため、機密情報を含めないこと

## 備考

- PushChannelはAbstractChannelを継承しており、ChatChannelと同様の構造を持つ
- PushMessage::fromNotification()ではsubjectとcontentの両方が使用される（ChatMessageと異なりcontentも含む）
- PushMessageクラスのメソッドはselfを返す（staticではない）点に注意（PushMessage.php 行44, 57, 68, 80）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PushNotificationInterface.php | `src/Symfony/Component/Notifier/Notification/PushNotificationInterface.php` | asPushMessage()のシグネチャ（行19）。RecipientInterfaceを受け取る |
| 1-2 | PushMessage.php | `src/Symfony/Component/Notifier/Message/PushMessage.php` | コンストラクタ（行24-29）でsubject, content, optionsを受け取る。fromNotification()（行31-37）でsubjectとcontentの両方を使用 |

**読解のコツ**: PushMessageはChatMessageと異なり、contentプロパティも持つ。fromNotification()でNotification::getContent()が使用される点がChatMessageとの違い。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Notifier.php | `src/Symfony/Component/Notifier/Notifier.php` | send()メソッド（行40-51）。PushChannelはChatChannelと同様にすべてのRecipientをサポート |

#### Step 3: チャネル処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PushChannel.php | `src/Symfony/Component/Notifier/Channel/PushChannel.php` | notify()（行24-42）の処理フロー。ChatChannelとほぼ同じ構造 |

**主要処理フロー**:
- **行27-29**: PushNotificationInterface実装時はasPushMessage()でカスタムメッセージ生成
- **行31**: デフォルトはPushMessage::fromNotification(notification)
- **行33-35**: transportName設定
- **行37-41**: Bus有無による送信方式の分岐

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

```
Notifier::send()
    |
    +-- getChannels()
    |
    +-- PushChannel::supports()  [常にtrue]
    |
    +-- PushChannel::notify()
            |
            +-- [PushNotificationInterface] notification->asPushMessage()
            |       または
            +-- PushMessage::fromNotification(notification)
            |       +-- new PushMessage(subject, content)
            |
            +-- PushMessage::transport() [transportName設定]
            |
            +-- [Bus == null] Transport::send(message)
            +-- [Bus != null] Bus::dispatch(message)
```

### データフロー図

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

Notification                    Notifier::send()
  - subject          -------->    |
  - content                       v
  - channels                   PushChannel::notify()
                                  |
RecipientInterface                v                    -------->  プッシュ通知送信
  （任意の実装）     -------->  PushMessage生成                    (Transport or Bus)
                                  |
MessageOptions                    v
  - recipientId      -------->  送信実行
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PushNotificationInterface.php | `src/Symfony/Component/Notifier/Notification/PushNotificationInterface.php` | ソース | Push通知インターフェース |
| PushChannel.php | `src/Symfony/Component/Notifier/Channel/PushChannel.php` | ソース | プッシュ通知送信チャネル |
| AbstractChannel.php | `src/Symfony/Component/Notifier/Channel/AbstractChannel.php` | ソース | チャネル基底クラス |
| PushMessage.php | `src/Symfony/Component/Notifier/Message/PushMessage.php` | ソース | プッシュメッセージクラス |
| Notification.php | `src/Symfony/Component/Notifier/Notification/Notification.php` | ソース | 通知基本クラス |
| Notifier.php | `src/Symfony/Component/Notifier/Notifier.php` | ソース | 通知送信エントリーポイント |
