# 通知設計書 17-NotificationEmail

## 概要

本ドキュメントは、Symfony Twig Bridgeが提供する `NotificationEmail` クラスの設計仕様を定義する。テンプレートベースの通知メール生成クラスであり、重要度（importance）に応じた件名プレフィックスとメール優先度の自動設定、Markdown対応、例外情報の添付ファイル化、テーマ切替等の機能を提供する。

### 本通知の処理概要

NotificationEmailは、TemplatedEmailを継承した通知専用メールクラスである。Notifierコンポーネントの `EmailChannel` からメール通知を送信する際の標準的なメール生成クラスとして機能し、TwigテンプレートによるHTML/テキストメールの自動レンダリング、重要度ベースの件名プレフィックス自動付与、メール優先度の自動マッピング等を提供する。

**業務上の目的・背景**：アプリケーションから送信される通知メールには、統一的なフォーマット、重要度に応じた視覚的な区別、例外情報の構造化された添付等が求められる。NotificationEmailはこれらの要件を標準化し、開発者が個別にメールフォーマットを設計する手間を削減する。Twigテンプレートによるレンダリングにより、HTMLメールとテキストメールの両方を一貫したデザインで生成できる。また、public/admin向けの使い分け機能により、外部ユーザーと管理者で異なる表示形式を提供できる。

**通知の送信タイミング**：NotificationEmailはメールオブジェクトであり、Notifierの `EmailChannel` や `Mailer` を通じて送信される。Notification::fromThrowable()等のファクトリーメソッドから生成されるケースや、開発者がアプリケーション内で直接インスタンスを生成して送信するケースがある。

**通知の受信者**：メールの宛先（To, CC, BCC）として設定されたメールアドレスの保有者。asPublicEmail()で生成された場合は一般ユーザー向け、デフォルトでは管理者向けのフォーマットとなる。

**通知内容の概要**：重要度（urgent/high/medium/low）、本文コンテンツ（プレーンテキストまたはMarkdown）、アクションボタン（テキストとURL）、例外情報（添付ファイル形式）、フッターテキスト、テーマ設定を含む通知メール。

**期待されるアクション**：受信者がメールを確認し、重要度に応じた対応（urgentの場合は即座の対応、lowの場合は参考程度の確認等）を行う。アクションボタンが含まれる場合は指定されたURLへの遷移が期待される。

## 通知種別

メール通知（HTML/テキスト、Twigテンプレートベース）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期/非同期（Mailerの設定に依存） |
| 優先度 | importanceに応じて自動設定（urgent→HIGHEST, high→HIGH, medium→NORMAL, low→LOW） |
| リトライ | Mailerのトランスポート設定に依存 |

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

Email（Mimeコンポーネント）のTo/CC/BCCヘッダーに設定されたアドレスに送信される。Notifier経由の場合、RecipientInterfaceから取得したメールアドレスが設定される。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | Mailerの設定に依存（envelope_sender等） |
| 送信元名称 | Mailerの設定に依存 |
| 件名 | `[{IMPORTANCE}] {subject}`（importance設定時。publicメールの場合はプレフィックスなし） |
| 形式 | HTML/テキスト（Twigテンプレートでレンダリング） |

### 本文テンプレート

```
HTMLテンプレート: @email/{theme}/notification/body.html.twig
テキストテンプレート: @email/{theme}/notification/body.txt.twig

デフォルトテーマ: default
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| exception.txt | text/plain | exception()メソッドが呼ばれた場合 | 例外のクラス名、メッセージ、ファイル、行番号、スタックトレースを含むテキストファイル |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| importance | 重要度（urgent/high/medium/low/null） | context['importance'] | No（デフォルト: low） |
| content | 本文コンテンツ | context['content'] | No（デフォルト: 空文字列） |
| exception | 例外情報の有無 | context['exception'] | No（デフォルト: false） |
| action_text | アクションボタンテキスト | context['action_text'] | No（デフォルト: null） |
| action_url | アクションボタンURL | context['action_url'] | No（デフォルト: null） |
| markdown | Markdown有効フラグ | context['markdown'] | No（デフォルト: false） |
| raw | HTMLタグ無エスケープフラグ | context['raw'] | No（デフォルト: false） |
| footer_text | フッターテキスト | context['footer_text'] | No（デフォルト: 'Notification email sent by Symfony'） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| アプリケーション通知 | Notifier::send()経由のEmailChannel送信 | EmailRecipientInterfaceを実装したRecipient | Notifierの通知フローからの送信 |
| 直接送信 | Mailer::send()での直接送信 | NotificationEmailオブジェクトの生成 | 開発者がアプリケーション内で直接使用 |
| 例外通知 | Notification::fromThrowable()経由 | 例外発生時 | 例外情報を含む通知メールの自動生成 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 必要パッケージ未インストール | twig/cssinliner-extraまたはtwig/inky-extraが未インストールの場合、コンストラクタでLogicException |
| twig/markdown-extra未インストール | markdown()メソッド使用時にtwig/markdown-extraが未インストールの場合、LogicException |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[NotificationEmail生成] --> B{パッケージチェック}
    B -->|不足| C[LogicException]
    B -->|OK| D[コンテンツ設定]
    D --> E{Markdown?}
    E -->|Yes| F[markdown有効化 + content設定]
    E -->|No| G[content設定]
    F --> H[importance設定]
    G --> H
    H --> I{例外あり?}
    I -->|Yes| J[exception.txt添付 + importance=urgent]
    I -->|No| K[テンプレートレンダリング]
    J --> K
    K --> L[getPreparedHeaders]
    L --> M{importanceあり?}
    M -->|Yes| N[件名プレフィックス付与 + priority設定]
    M -->|No| O[priority設定のみ]
    N --> P[メール送信]
    O --> P
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| 該当なし | - | NotificationEmailクラス自体はDB参照を行わない |

### テーブル別参照項目詳細

該当なし。NotificationEmailはメールオブジェクトであり、データベースアクセスは行わない。

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| 該当なし | - | メールオブジェクトのためDB更新なし |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| LogicException（パッケージ不足） | twig/cssinliner-extraまたはtwig/inky-extraが未インストール | composer requireで必要パッケージをインストール |
| LogicException（Markdown） | twig/markdown-extraが未インストールでmarkdown()を呼び出し | composer require twig/markdown-extraを実行 |
| レンダリングエラー | Twigテンプレートのレンダリングに失敗 | テンプレートファイルの存在確認とテンプレート構文の修正 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Mailerのトランスポート設定に依存 |
| リトライ間隔 | Mailerのトランスポート設定に依存 |
| リトライ対象エラー | トランスポート送信エラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | Mailerのトランスポートレート制限に依存 |
| 1日あたり上限 | Mailerのトランスポート設定に依存 |

### 配信時間帯

Mailerの設定に依存。NotificationEmail自体に配信時間帯の制限機能はない。

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

- content()メソッドのraw=trueオプションはHTMLタグを無エスケープで出力するため、ユーザー入力を直接渡す場合はXSSリスクがある
- exception()メソッドで添付される例外情報にはスタックトレースやファイルパス等のシステム内部情報が含まれるため、管理者向けメールにのみ使用すること
- asPublicEmail()/markAsPublic()で一般ユーザー向けとする場合、importanceとfooter_textがnullに設定される
- 例外情報にはデータベースの接続情報やAPI鍵等のセンシティブ情報が含まれる可能性があるため、FlattenExceptionでサニタイズされた形式を使用することが推奨される

## 備考

- NotificationEmailは `Symfony\Bridge\Twig\Mime\NotificationEmail` に定義されている
- TemplatedEmail -> Email -> Message -> RawMessage のクラス階層を持つ
- importance -> priority のマッピング: urgent→HIGHEST, high→HIGH, medium→NORMAL, low(default)→LOW（行227-234）
- コンストラクタで `CssInlinerExtension` と `InkyExtension` の存在チェックを行う（行47-58）
- シリアライゼーション対応: __serialize()/__unserialize()でcontext, theme, renderedを永続化（行259-279）
- themeプロパティにより通知メールのテンプレートテーマを切り替え可能（デフォルト: 'default'）

---

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

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

### 推奨読解順序

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

NotificationEmailのクラス階層とコンテキスト変数の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | TemplatedEmail.php | `src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php` | 親クラス。htmlTemplate, textTemplate, locale, contextの4プロパティを管理（行19-116） |
| 1-2 | NotificationEmail.php | `src/Symfony/Bridge/Twig/Mime/NotificationEmail.php` | 本体クラス。4つの重要度定数、theme、context（7つのキー）、renderedフラグを管理（行25-280） |

**読解のコツ**: NotificationEmailは独自のcontextプロパティを持ち、親クラスのcontextとはマージして管理される。context()メソッド（行180-195）で、NotificationEmail固有のキー（importance, content等）は自身のcontextに、それ以外は親クラスのcontextに振り分けられる点に注目。

#### Step 2: 重要度マッピングを理解する

重要度（importance）からメール優先度（priority）へのマッピングと件名プレフィックス付与を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | NotificationEmail.php | `src/Symfony/Bridge/Twig/Mime/NotificationEmail.php` | getPreparedHeaders()（行214-225）で重要度に応じた件名プレフィックスとメール優先度を設定。determinePriority()（行227-234）でimportanceからpriorityへの変換 |

**主要処理フロー**:
1. **行218**: `$this->context['importance']` から重要度を取得（nullの場合はデフォルトlow）
2. **行219**: `$this->priority($this->determinePriority($importance))` で優先度設定
3. **行220-222**: importanceがnullでない場合、`[{IMPORTANCE}] {subject}` 形式で件名を再設定
4. **行229-233**: match式でurgent→HIGHEST, high→HIGH, medium→NORMAL, default→LOWに変換

#### Step 3: 例外情報の添付処理を理解する

exception()メソッドによる例外情報の添付と自動設定を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NotificationEmail.php | `src/Symfony/Bridge/Twig/Mime/NotificationEmail.php` | exception()メソッド（行134-147）で例外情報をテキストファイルとして添付し、importanceをurgentに設定 |

**主要処理フロー**:
1. **行136**: `getExceptionAsString()` で例外を文字列に変換
2. **行138**: `context['exception'] = true` を設定
3. **行139**: `addPart(new DataPart($exceptionAsString, 'exception.txt', 'text/plain'))` で添付
4. **行140**: `importance(self::IMPORTANCE_URGENT)` で重要度をurgentに設定
5. **行142-143**: 件名が未設定の場合、例外メッセージを件名に設定

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

Twigテンプレートの解決ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | NotificationEmail.php | `src/Symfony/Bridge/Twig/Mime/NotificationEmail.php` | getTextTemplate()（行159-166）とgetHtmlTemplate()（行168-175）。親クラスでテンプレートが設定されている場合はそれを使用し、未設定の場合はtheme付きのデフォルトパスを返す |

**主要処理フロー**:
- **行161**: 親クラスのgetTextTemplate()を呼び出し
- **行165**: 未設定の場合 `@email/{theme}/notification/body.txt.twig` を返す
- **行171**: 親クラスのgetHtmlTemplate()を呼び出し
- **行174**: 未設定の場合 `@email/{theme}/notification/body.html.twig` を返す

#### Step 5: publicメール機能を理解する

管理者向けと一般ユーザー向けの切り替えを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | NotificationEmail.php | `src/Symfony/Bridge/Twig/Mime/NotificationEmail.php` | asPublicEmail()（行66-72）とmarkAsPublic()（行77-83）。publicモードではimportanceとfooter_textがnullになる |

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

```
NotificationEmail (生成)
    |
    +-- __construct()
    |       |
    |       +-- CssInlinerExtension存在チェック
    |       +-- InkyExtension存在チェック
    |       +-- parent::__construct()
    |
    +-- content() / markdown() / action() / importance() / exception() / theme()
    |       |
    |       +-- context配列の更新
    |       +-- [exception] DataPart添付 + importance=urgent
    |
    +-- getPreparedHeaders()  [送信前に呼ばれる]
    |       |
    |       +-- parent::getPreparedHeaders()
    |       +-- determinePriority($importance)  [importance -> priority変換]
    |       +-- [importance設定時] 件名プレフィックス付与
    |
    +-- getHtmlTemplate() / getTextTemplate()  [テンプレートレンダリング時]
    |       |
    |       +-- 親テンプレート確認 → デフォルトテーマテンプレート
    |
    +-- getContext()
            |
            +-- array_merge(自身のcontext, 親のcontext)
```

### データフロー図

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

importance ----+
content -------+
exception -----+----> NotificationEmail::context配列
action_text ---+          |
action_url ----+          +----> [exception時] DataPart生成
markdown ------+          |          |
footer_text ---+          |          +----> exception.txt添付
theme ---------+          |
                          +----> getPreparedHeaders()
                          |          |
                          |          +----> importance → priority変換
                          |          +----> [importance] 件名プレフィックス
                          |
                          +----> getHtmlTemplate() / getTextTemplate()
                          |          |
                          |          +----> @email/{theme}/notification/body.html.twig
                          |          +----> @email/{theme}/notification/body.txt.twig
                          |
                          +----> Twigレンダリング
                                     |
                                     +----> HTML/テキストメール本文
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| NotificationEmail.php | `src/Symfony/Bridge/Twig/Mime/NotificationEmail.php` | ソース | 通知メールクラス本体 |
| TemplatedEmail.php | `src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php` | ソース | テンプレートメール親クラス |
| FlattenException.php | `src/Symfony/Component/ErrorHandler/Exception/FlattenException.php` | ソース | 例外のフラット化（例外情報の安全な文字列化） |
| DataPart.php | `src/Symfony/Component/Mime/Part/DataPart.php` | ソース | 添付ファイルパート |
| CssInlinerExtension | twig/cssinliner-extra | 外部パッケージ | CSSインライン化（必須） |
| InkyExtension | twig/inky-extra | 外部パッケージ | Inkyテンプレートエンジン（必須） |
| MarkdownExtension | twig/markdown-extra | 外部パッケージ | Markdown変換（markdown()使用時に必須） |
