build()実行タイミングの罠
こんにちは😊
Laravelでメール送信処理を書いていたとき、ログの順序が想定と異なって戸惑ったことがありました。
地味ですが実務で「えっ?」となる場面があるので、共有しておきます。
目次
問題のコード
ある処理でメール送信をしており、以下のようなコードを書いていました。
$mailContent = new TestMailable($hoge); // メールインスタンス生成
Log::debug("後に出るはず"); // ログ出力
Mail::send($mailContent); // メール送信
そして、送信するMailableクラスはこんな感じ👇
class TestMailable extends Mailable
{
public function __construct($huga)
{
// 初期化処理
}
public function build(): static
{
Log::debug("先に出るはず");
return $this->view('emails.test');
}
}
実際に出力されたログ
[2025-07-24 16:32:40] local.DEBUG: 後に出るはず
[2025-07-24 16:32:40] local.DEBUG: 先に出るはず
・・・えっ!?ログが逆になってる!😱
後輩からの質問とモヤモヤ
後輩「なんで順番逆になるんですか?非同期処理ですか?」
自分「え、PHPはスクリプト言語だから逐次実行じゃないの…?
もしかして Queueable の影響かも…(ゴニョゴニョ)」
と、言い切れずにその場を濁してしまいました🥲
原因をちゃんと調べた
悔しかったので、ちゃんとソースまで確認しました💪
結論:
Mailable クラスの build()
は、Mail::send()
のタイミングで実行される!
🧵 処理の実行順序はこうなる
$mailContent = new TestMailable($hoge); // __construct() 実行
↓
Log::debug("後に出るはず"); // ログ出力
↓
Mail::send($mailContent); // build() 実行 → ログ出力
↓
Log::debug("先に出るはず");
💡 Laravelソースコードでも確認
Laravelの Mailer クラスを確認すると、send()
の内部で build()
が呼ばれていることがわかります。
if (method_exists($view, 'build')) {
$view = $view->build();
}
つまり、build()
は new
の時点では実行されていません!
解決方法
順序通りにログを出したい場合は、単純にこうすればOK👌
$mailContent = new TestMailable($hoge);
Mail::send($mailContent);
Log::debug("後に出るはず");
学び
これまであまり意識していませんでしたが…
Mailable::build()
は 送信時に初めて実行される
という仕様です📌
「インスタンス化された時点でメールの中身が決まるわけではない」
これは実際に動かしてみないと意外と気づきにくい仕様ですね。
おわりに
今回の話はニッチかもしれませんが、チーム開発ではログの順序が想定と違うだけで「あれ?」となることもあります。
即答できなかった自分が悔しかったので、改めてちゃんと調べて理解を深めました🔥
同じように「なんで?」と思った方の参考になればうれしいです。
参考リンク
Laravel公式ドキュメント - Mailable
https://laravel.com/docs/12.x/mail
Laravel GitHubソース - Illuminate\Mail\Mailer
https://github.com/laravel/framework/blob/11.x/src/Illuminate/Mail/Mailer.php
ご意見・コメント・「わかる!」などあればぜひお寄せください!🙏
最後まで読んでいただきありがとうございました!✨