# 通知設計書 47-Mail Before Sent

## 概要

本ドキュメントは、ShopwareにおけるMailBeforeSentEventの仕様を定義する設計書である。これはメール通知ではなく、メール送信直前に発火されるイベント通知であり、開発者が最終的なメールオブジェクト（Symfony Email）を変更するためのフックポイントを提供する機能について記載する。

### 本通知の処理概要

本イベントは、MailServiceがSymfony Emailオブジェクトを作成した後、実際の送信処理を実行する直前に発火されるシステムイベントである。MailBeforeValidateEventとは異なり、このイベントではすでに構築されたEmailオブジェクトに直接アクセスできるため、ヘッダーの追加、添付ファイルの操作、本文の最終調整などが可能である。

**業務上の目的・背景**：メールの最終的な内容をカスタマイズしたい場合、テンプレート変数レベルの変更（MailBeforeValidateEvent）では対応できないケースがある。例えば、カスタムヘッダーの追加、BCC受信者の追加、署名の動的追加、特定のMIMEタイプ設定などは、Emailオブジェクト作成後でなければ行えない。本イベントはこれらのユースケースに対応するためのフックポイントを提供する。

**通知の送信タイミング**：MailService::send()メソッド内で、Emailオブジェクト作成・バリデーション完了後、MailSender::send()呼び出し直前に発火される。

**通知の受信者**：イベントリスナーとして登録されたPHPクラス。プラグインやカスタムコードからEventSubscriberとして登録する。

**通知内容の概要**：作成されたEmailオブジェクト、送信データ配列、Contextオブジェクト、イベント名が含まれる。

**期待されるアクション**：イベントリスナーはEmailオブジェクトを直接変更し、必要に応じてイベントの伝播を停止（stopPropagation）することでメール送信を抑止できる。

## 通知種別

イベント通知（システムイベント）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| イベント方式 | 同期（EventDispatcher） |
| 発火タイミング | メール送信直前（Emailオブジェクト作成後） |
| 伝播停止 | 可能（stopPropagation） |

### イベントリスナー登録方法

```php
use Shopware\Core\Content\MailTemplate\Service\Event\MailBeforeSentEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class MyMailSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            MailBeforeSentEvent::class => 'onMailBeforeSent',
        ];
    }

    public function onMailBeforeSent(MailBeforeSentEvent $event): void
    {
        $message = $event->getMessage();

        // カスタムヘッダー追加
        $message->getHeaders()->addTextHeader('X-Custom-Header', 'value');

        // BCC追加
        $message->addBcc('admin@example.com');
    }
}
```

## イベントプロパティ

### イベントクラス

| プロパティ | 型 | 説明 | 変更可能 |
|-----------|-----|------|---------|
| data | array | メール送信データ | No（readonly） |
| message | Email | Symfony Emailオブジェクト | Yes（参照） |
| context | Context | Shopwareコンテキスト | No |
| eventName | string|null | トリガーイベント名 | No |

### data配列の構造

MailBeforeValidateEventと同一。

### Emailオブジェクトの操作

| メソッド | 説明 |
|---------|------|
| getMessage() | Symfony Emailオブジェクト取得 |
| message->getHeaders() | ヘッダーコレクション取得 |
| message->addBcc() | BCC受信者追加 |
| message->addCc() | CC受信者追加 |
| message->addPart() | MIMEパート追加 |
| message->getHtmlBody() | HTML本文取得 |
| message->getTextBody() | テキスト本文取得 |

## テンプレート変数

本イベントはメールテンプレートを使用しないため、該当なし。（テンプレートレンダリングは完了済み）

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 発火条件 | 説明 |
|------------|----------------|---------|------|
| MailService | send()メソッド | Emailオブジェクト作成成功 | バリデーション通過後 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| stopPropagation() | リスナーがイベント伝播を停止した場合、メール送信がスキップされる |

## 処理フロー

### イベントフロー

```mermaid
flowchart TD
    A[MailService::send] --> B[MailBeforeValidateEvent発火]
    B --> C[バリデーション実行]
    C --> D[Emailオブジェクト作成]
    D --> E[MailBeforeSentEvent生成]
    E --> F[EventDispatcher::dispatch]
    F --> G{リスナー処理}
    G --> H[リスナー1: ヘッダー追加]
    H --> I[リスナー2: BCC追加]
    I --> J{stopPropagation?}
    J -->|Yes| K[メール送信スキップ return null]
    J -->|No| L[MailSender::send実行]
    L --> M[MailSentEvent発火]
    K --> N[終了]
    M --> N
```

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

### 参照テーブル一覧

本イベント自体はデータベースを参照しない。

### 更新テーブル一覧

本イベント自体はデータベースを更新しない。

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| リスナー例外 | イベントリスナー内で例外発生 | 例外伝播、メール送信中断 |
| Emailオブジェクト破損 | リスナーがEmailを不正に変更 | 送信時例外発生 |

### リトライ仕様

本イベントはリトライ対象外。同期処理で即座に完了する。

## 配信設定

### レート制限

本イベントにレート制限なし。

### 配信時間帯

制限なし。メール送信リクエスト発生時に即座に発火。

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

- Emailオブジェクトへの直接アクセスは強力な機能のため、信頼されたコードのみ登録すべき
- BCC追加による情報漏洩リスクに注意
- カスタムヘッダーによるメールクライアントへの影響に注意

## 備考

- イベント名定数：MailBeforeSentEvent::EVENT_NAME = 'mail.after.create.message'
- LogAwareインターフェース実装によりMonolog経由でログ出力可能
- MessageAwareインターフェース実装によりEmailオブジェクトへのアクセスを提供
- FlowEventAware実装によりFlowビルダーでの利用も可能

---

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

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

### 推奨読解順序

#### Step 1: イベントクラスを理解する

イベントクラスの構造とインターフェースを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | MailBeforeSentEvent.php | `src/Core/Content/MailTemplate/Service/Event/MailBeforeSentEvent.php` | イベント名（23行目）、コンストラクタ（28-33行目）、getMessage()（64-67行目） |

**読解のコツ**: LogAware, MessageAware, ScalarValuesAware, FlowEventAwareを実装。messageプロパティはSymfony\Component\Mime\Email型で、直接操作可能。

#### Step 2: イベント発火箇所を理解する

MailServiceでのイベント発火タイミングを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MailService.php | `src/Core/Content/Mail/Service/MailService.php` | send()メソッド内（103-107行目）でのイベント発火 |

**主要処理フロー**:
1. **75行目**: createMail() - Emailオブジェクト作成
2. **103行目**: MailBeforeSentEvent生成（$data, $mail, $context, $eventName）
3. **104行目**: eventDispatcher->dispatch() - イベント発火
4. **105-107行目**: isPropagationStopped()チェック - 停止時はnull返却
5. **110行目**: mailSender->send() - 実際の送信

#### Step 3: MessageAwareインターフェースを理解する

Emailオブジェクトへのアクセス方法を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MailBeforeSentEvent.php | `src/Core/Content/MailTemplate/Service/Event/MailBeforeSentEvent.php` | getMessage()（64-67行目）でEmailオブジェクト取得 |

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

```
MailService::send($data, $context, $templateData)
    │
    ├─ MailBeforeValidateEvent dispatch
    │
    ├─ dataValidator->validate()
    │
    ├─ createMail() - Emailオブジェクト作成
    │      └─ mailFactory->create()
    │
    ├─ new MailBeforeSentEvent($data, $mail, $context, $eventName)
    │
    ├─ eventDispatcher->dispatch(MailBeforeSentEvent)
    │      │
    │      ├─ EventSubscriber A: onMailBeforeSent()
    │      │      └─ $event->getMessage()->getHeaders()->addTextHeader()
    │      │
    │      ├─ EventSubscriber B: onMailBeforeSent()
    │      │      └─ $event->getMessage()->addBcc()
    │      │
    │      └─ EventSubscriber C: onMailBeforeSent()
    │             └─ $event->stopPropagation() (オプション)
    │
    ├─ if ($event->isPropagationStopped()) return null
    │
    └─ mailSender->send($mail) - 実際の送信
```

### データフロー図

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

Emailオブジェクト               MailBeforeSentEvent                  変更後Email
  │                                  │                                    │
  ├─ subject                         ├─ EventDispatcher                   ├─ 追加ヘッダー
  ├─ recipients                      │                                    ├─ 追加BCC/CC
  ├─ htmlBody                        ├─ Subscribers                       └─ カスタム設定
  ├─ textBody                        │    ├─ getMessage()
  └─ headers                         │    └─ 直接変更
                                     │
                                     └─ 伝播制御
                                          │
                                          ├─ 続行: MailSender::send()
                                          └─ 停止: return null
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| MailBeforeSentEvent.php | `src/Core/Content/MailTemplate/Service/Event/MailBeforeSentEvent.php` | ソース | イベントクラス定義 |
| MailService.php | `src/Core/Content/Mail/Service/MailService.php` | ソース | イベント発火箇所 |
| MessageAware.php | `src/Core/Content/Flow/Dispatching/Aware/MessageAware.php` | インターフェース | Emailアクセスインターフェース |
| BusinessEventRegistry.php | `src/Core/Framework/Event/BusinessEventRegistry.php` | ソース | イベント登録 |
