新人でもわかる！Laravel メール認証パイプラインをコードで追いかけてみた

## 導入
個人開発にハマり中の新卒のエンジニアです。
簡単な自己紹介は以下に
- 私立文系卒
    - 大学2年の冬からプログラミング
    - 学生時代は1年半ほどプログラミングスクールでインターン
- めちゃめちゃ柏レイソルサポーター
    - 本当に今年は最高

本記事ではLaravelメール認証のコードリーディングを進めていきます。

**「メール認証について理解を深めたい」**

こんな人に読んでいただけたら光栄です。

## sendEmailVerificationNotification()

まず最初に以下のメソッドを読み込んでいこうと思います。
```
$request->user()->sendEmailVerificationNotification();
```

こちらのコードは、メール認証の際に、登録リクエスト後に動くメソッドです。
パッと見ると、リクエストで送られたユーザ情報をもとに、認証用のメール送っているんだな、くらいは命名から分かります。

**ではこれがどう動くのか、君は知りたくないか？？**


### リポジトリ

LaravelのIlluminateは、様々なサービスやコンポーネントを構成する基盤を構築しています。
そのため、フレームワークの恩恵である「すでに出来上がっている部分」への理解を深めるには、Illuminateを掘り下げていくことが多いです。
※他にもいろいろあります。


https://github.com/laravel/framework/tree/12.x/src/Illuminate

::: note warn
本来は、以下のorganizationにあるはずなのですが、なぜかFoundationコンポーネントがありませんでした。
https://github.com/illuminate
:::

### このメソッドどこにあるん
このメソッドが記されている箇所を探すのは少し大変でした。
frameworkリポジトリの以下のパスにありました。
```
framework/src/Illuminate/Auth/MustVerifyEmail.php
```
このトレイトはUserモデルで呼ばれています。
トレイトについて知りたい方は以下の記事を参考にしてください。

https://qiita.com/nanami173/items/051140e66a6f328a59bc

MustVerifyEmailトレイトには、以下のようにsendEmailVerificationNotificationメソッドが定義されています。
```
use Illuminate\Auth\Notifications\VerifyEmail;

//中略

public function sendEmailVerificationNotification()
{
    $this->notify(new VerifyEmail);
}
```
ちなみにnotifyメソッドもトレイトです。
今はこのnotifyについては深堀しません。
引数に受けた通知インスタンスごとに、通知を走らせてくれると覚えてください。
### このVerifyEmailとは何ぞや

VetifyEmailクラスを覗いていきます。
framework/src/Illuminate/Auth/Notifications/VerifyEmail.php
```
<?php

namespace Illuminate\Auth\Notifications;

use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\URL;

class VerifyEmail extends Notification
{
//中略
}
```
このクラスの中身には、コンストラクタなどが見当たりませんでした。
先ほど取り上げたnotify()の引数では、ただインスタンスが生成されているだけでした。

ですが、おそらく認証用メールに関する内容はこのクラスの責務だと思われます。
そこは内容ではないので、省きます。

それでは特にVerifyEmailインスタンスの処理は走らないのでは、やはりnotifyに仕組みがあるのか
どうやらnotify()を掘り下げていく必要がありそうです。
### notifyメソッドの仕組み
notifyメソッドは、RoutesNotificationsトレイトに定義されたメソッドです。
framework/src/Illuminate/Notifications/RoutesNotifications.php
※notifyメソッド以外は省きます。
```
<?php

namespace Illuminate\Notifications;

use Illuminate\Contracts\Notifications\Dispatcher;
use Illuminate\Support\Str;

trait RoutesNotifications
{
    public function notify($instance)
    {
        app(Dispatcher::class)->send($this, $instance);
    }
}
```

ここにnotifyメソッドが定義されていました。
見てみると、サービスコンテナにバインドされているDispatcherクラスを呼び出して、引数のインスタンスをsendメソッドで送信しているんだろうなと予想できます。
では次にDispatcherクラスを見ようかと思いましたが、、

#### Dispatcherはただのインターフェース
framework/src/Illuminate/Notifications/Dispatcher.php
```
<?php

namespace Illuminate\Contracts\Notifications;

interface Dispatcher
{
    public function send($notifiables, $notification);
    public function sendNow($notifiables, $notification, ?array $channels = null);
}
```

Dispatcherはただのインターフェースでした。
もう意味が分からん（笑）
ですが、ふと思い出しました。
学生時代にお世話になっていた企業のプロダクトでは、リポジトリ層でインターフェースとバインドしていました。
（なぜこんな回りくどいことをするのだろうと、最初は本当に意味分からんかった）

プロバイダーを覗いてみると、DispatcherクラスはChannelManagerクラスにバインドされていました。
#### ChannelManagerクラス

ついに見つけた、sendメソッド！！

framework/src/Illuminate/Notifications/ChannelManager.php
```
<?php

namespace Illuminate\Notifications;

use Illuminate\Contracts\Bus\Dispatcher as Bus;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Notifications\Dispatcher as DispatcherContract;
use Illuminate\Contracts\Notifications\Factory as FactoryContract;
use Illuminate\Support\Manager;
use InvalidArgumentException;

class ChannelManager extends Manager implements DispatcherContract, FactoryContract
{
    //中略
    public function send($notifiables, $notification)
    {
        (new NotificationSender(
            $this, $this->container->make(Bus::class), $this->container->make(Dispatcher::class), $this->locale)
        )->send($notifiables, $notification);
    }
    //中略
}

```

ここがどうやら、Laravelにおける通知の「司令塔」として動作しているようです。

インスタンスが生成されたNotificationSenderは主に以下の役割を果たしています。
1. 宛先の正規化
オブジェクトを配列に変換し、ループしやすくする。

1. キュー判定
通知クラスが `ShouldQueue` を実装していれば `queueNotification()`、そうでなければ `sendNow()` を呼ぶ。

1. チャネル決定
`via($notifiable)` が返す配列（例: `['mail', 'database']`）をもとに、対象ユーザ × チャネル の組み合わせを走査。

1. 実配送
各チャネルごとに `ChannelManager::driver($channel)->send()` を実行し、具体的な `MailChannel` や `DatabaseChannel` が処理。

1. イベント発火
送信前に `NotificationSending`、後に `NotificationSent` を `EventDispatcher` に投げるので、ロギングやキャンセルが可能。

### 定義元を追い求めて、

以下の順番で追いかけてきました
1. `framework/src/Illuminate/Auth/MustVerifyEmail->sendEmailVerificationNotification()`
1. `framework/src/Illuminate/Notifications/RoutesNotifications->notify()`
1. `framework/src/Illuminate/Notifications/Dispatcher.php`
1. `framework/src/Illuminate/Notifications/ChannelManager.php`

掘り下げようと思えば、まだいけるでしょう。

次回の記事では、上で少し説明を省いたNotificationSenderクラスを読んでいきます。

## まとめ
クラスの構造は詳細設計などで十分にやると思いますが、フレームワークの基幹部分を読み込んでいくと、本当にレベルが違いますね。

フレームワークの開発者、OSSコントリビュータには頭が上がりません。