【Mailable】Laravelでメール送信する最適解【2024年夏最新版】


[前回](https://qiita.com/sigeta/items/54f87c035b92f056816f) のメール編！
Laravelで困るのって、書類発行とメール送信だと思う。
（JobやWebSocketから目をそらして）

https://readouble.com/laravel/11.x/ja/mail.html

----

# 1. メール設定

Laravel は標準でメール送信に対応している。
まず、基本設定についてまとめる。
設定中の動作確認は「2. メールの送り方」を参照のこと。


## .env 設定値

設定は `config/mail.php` と `.env` の `MAIL_` で始まるキーで定義されている。

```ini:.env
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
```

### :pushpin: `MAIL_MAILER`

`config/mail.php` の `mailers` で定義されているドライバ情報を記入する。  

基本的には `smtp` で良い。
Amazon SES を使うのであれば `ses`。
`log` にすると、Laravel のログに出力されるので便利。

### :pushpin: `MAIL_HOST` `MAIL_PORT` `MAIL_USERNAME` `MAIL_PASSWORD` `MAIL_ENCRYPTION`

接続先の情報。
メール送信に使用するサーバーやサービスの情報を記載する。
sail 環境であれば、`mailpit` コンテナを使用する。

`MAIL_ENCRYPTION` は `tls` `ssl` `null` の値を取る。
困ったら `tls` で動くはず。
適切に設定しないと、エラーで弾かれることが多い。


### :pushpin: `MAIL_FROM_ADDRESS` `MAIL_FROM_NAME`

送信元の情報。
基本的には送信に利用するアドレス情報を指定すればよい。
一括して `support@xxx` みたいなアドレスを使用したい場合、こちらに記載する。

※ドメインが異なる場合、セキュリティの都合で弾かれることが多い。
　ちゃんと存在するアドレスを指定しよう。






## 一般的なメールサーバーの場合

基本的には、メーラーに設定するSMTP情報を使うことで、メール送信が可能になる。
「Xserver Business」の場合、こちらに記載があった。

https://support.xserver.ne.jp/manual/man_mail_setting.php

使用するアドレスのアカウント情報と合わせて、設定値を作成する。

```ini:.env
MAIL_MAILER=smtp
MAIL_HOST="sv***.xbiz.ne.jp"
MAIL_PORT=587
MAIL_USERNAME="xxxxxx@example.com"
MAIL_PASSWORD="xxxxxxxx"
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="xxxxxx@example.com"
MAIL_FROM_NAME="${APP_NAME}"
```


## Gmail の場合 (アプリパスワード)

以前であれば、「一般的なメールサーバーの場合」と同様に設定できたのだが、セキュリティ強化の影響で使用できなくなるみたい...。

> https://support.google.com/a/answer/14114704?hl=ja  
> 2024 年秋:  
> すべての Google アカウントで、安全性の低いアプリへのアクセスが無効になります。  
> CalDAV、CardDAV、IMAP、SMTP、POP で従来のパスワード（基本認証）を使用できなくなります。  
>   
> （アプリパスワードは例外です）

でもアプリパスワードは使えるみたい！  
セキュリティ的には脆弱になるので、安全性を高めるには下の OAuth 認証方式を使おう。

[https://myaccount.google.com/security](https://myaccount.google.com/security)

まず使いたいアカウントの管理画面にアクセスして、「セキュリティ」「二段階認証プロセス」を選択する。
※あらかじめ二段階認証を有効にする必要があります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833190/00eb1926-b7b1-8bfe-01da-ddae14e73017.png" width="400px">

ページ下部の「アプリパスワード」を選択する。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833190/cf2b52c8-ddcb-198b-6efc-8188cf285c6f.png" width="400px">

任意の名称を付けると、パスワードが生成される。
二度と閲覧できないので記録しよう。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833190/252c0f97-d843-0d7c-e0a6-5298346f3eed.png" width="400px">

これらの値より、設定値を作成する。
SMTP情報はこちら。

https://support.google.com/mail/answer/7104828?hl=ja

```ini:.env
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME="xxxxxx@gmail.com"
MAIL_PASSWORD="xxxxxxxx"
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="xxxxxx@gmail.com"
MAIL_FROM_NAME="${APP_NAME}"
```

`MAIL_PASSWORD` に先ほど取得したアプリパスワードを記入する。




## Gmail の場合 (OAuth)

他の人にお任せする！  
この方が分かりやすいかと。  

https://zenn.dev/ad5/articles/3f299ff3fb604a

これ！っていうライブラリが無いから紹介しにくい...。


## Amazon SES の場合

AWS　にて SES のアカウント設定が必要。（割愛）

```bash
$ composer require aws/aws-sdk-php
```

設定値はこんな感じ。
MAIL 設定のほかに AWS の IAM 設定が必要になる。
意外とシンプルに扱える。

```ini:.env
AWS_ACCESS_KEY_ID="xxxxxxxx"
AWS_SECRET_ACCESS_KEY="xxxxxxxx"
AWS_DEFAULT_REGION="ap-northeast-1"

MAIL_MAILER=ses
MAIL_FROM_ADDRESS="xxxxxx@example.com"
MAIL_FROM_NAME="${APP_NAME}"
```

あとは他の人にお任せする！  
この方が分かりやすいかと。  

https://qiita.com/hiroaki-u/items/986b523998d5e415fc4d









# 2. メールの送り方

一般的にSMTP送信時に行うことは何でもできる。
メールは動作確認が大事なので、送信テスト用コマンドを作っておくと良い。



## シンプルにメールを送る

実はドキュメントに乗っていない方法。
というか Model と Blade を使う方法しか載ってない。
ただ送るだけの時に作るの面倒なので、こちらを用意。

```php
use Illuminate\Console\Command;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Message;
use Illuminate\Support\Facades\Mail;
use Symfony\Component\Mime\Address;

Mail::raw('本文です。', function (Message $message) {
    $message
        ->to([
            new Address('test@example.com', 'Lara'),
            new Address('sub@example.com', 'Vel'),
        ])
        ->subject('タイトル')
        ->attach(
            Attachment::fromStorage('red.png')
                ->as('赤い画像.png')
                ->withMime('image/png')
        );
});
```

生成されるメール本文はこちら。

```yaml
From: Laravel <xxxxxxxx@gmail.com>
To: Lara <test@example.com>, Vel <sub@example.com>
Subject: =?utf-8?Q?=E3=82=BF=E3=82=A4=E3=83=88=E3=83=AB?=
MIME-Version: 1.0
Date: Mon, 16 Sep 2024 03:29:33 +0000
Message-ID: <dfca79409fde9d1630bd1fbe206b1925@gmail.com>
Content-Type: multipart/mixed; boundary=tOuA3Ufv

--tOuA3Ufv
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

本文です。
--tOuA3Ufv
Content-Type: image/png;
 name="=?utf-8?Q?=E8=B5=A4=E3=81=84=E7=94=BB=E5=83=8F=2Epng?="
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 name*=utf-8''%E8%B5%A4%E3%81%84%E7%94%BB%E5%83%8F.png;
 filename*=utf-8''%E8%B5%A4%E3%81%84%E7%94%BB%E5%83%8F.png

iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACx
jwv8YQUAAAAJcEhZcwAADsEAAA7BAbiRa+0AAAANSURBVBhXY3gro/IfAAVUAi3GPZKdAAAAAElF
TkSuQmCC
--tOuA3Ufv--
```

詳しい関数はソースを見たほうが早い。

https://github.com/laravel/framework/blob/11.x/src/Illuminate/Mail/Message.php


### :pushpin: `to` `cc` `bcc`

`to($address, $name = null, $override = false)`

宛先情報の追加。
送信先を Address で作って、配列で指定する。
複数実行した場合、過去の値はそのままに追加される。

文字列指定もできるが、挙動が若干複雑なのでこっちをおすすめ。
Address は Symfony のほうを使う。
（多分 raw の使用は考慮されてない）

※ Address の代わりに User モデルを指定することも可能！


### :pushpin: `from` (`sender`)

`from($address, $name = null)`

サンプルでは指定していないが、送信元も指定できる。
未指定の場合は `.env` で指定した値が使用される。

`sender()` は意識する機会あるのかな？


### :pushpin: `subject`

`subject($subject)`

メールのタイトル。
ちなみに本文は `raw()` の第一引数なので注意。


### :pushpin: `attachment`

`attach($file, array $options = [])`

添付ファイル。
Attachment を使ってファイルを添付することができる。
添付元は `fromStorage()` `fromStorageDisk()` `fromData()` `fromPath()` と、必要そうなものは大体用意されている。
ちゃんと設定すれば `s3` から直接添付もできるんじゃないかな。

ファイル名未指定時は、元のファイル名がそのまま使われる。
Mime未指定時は、拡張子から勝手に推測されるみたい。（未検証）

いくつか付与したい場合は、何回も呼び出そう。

また zip にしたい場合は、別途 zip ライブラリを使って先に固める必要がある。




## Mailable + Blade を使ってメールを送る

メール送信用のテンプレートを作って、それを呼び出す一般的な使い方。
個人的には準備するものが多くなるので、インラインでも良くないか？と思っている。

テンプレートはコマンドで生成可能。

```bash
$ php artisan make:mail TestMail

$ php artisan make:view mail/testMailView
```

```php:resources/views/mail/testMailView.blade.php
<div>
    <p>こんにちは！ {{ $name }} さん</p>
</div>
```

```php:app/Mail/TestMail.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class TestMail extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct(protected string $name)
    {
        //
    }

    public function envelope(): Envelope
    {
        return new Envelope(
            subject: 'テストメール',
        );
    }

    public function content(): Content
    {
        return new Content(
            view: 'mail.testMailView',
        );
    }

    public function attachments(): array
    {
        return [
            Attachment::fromStorage('red.png')
                ->as('赤い画像.png')
                ->withMime('image/png')
        ];
    }
}
```

使い方。

```php
$res = Mail::to([
        new Address('test@example.com', 'Lara'),
        new Address('sub@example.com', 'Vel'),
    ])
    ->send(new TestMail('たなかさん'));
```

to などの仕様は `Mail::raw()` と同等。  


### :pushpin: `envelope`

宛名情報を記録する場所。
基本は `subject` のメール件名を記載する場所。
他にも `from` や `tags` `metadata` などが指定可能。


### :pushpin: `content`

本文を記載する場所。
`view` モードと `markdown` モードに対応している。

`text` を指定しておくと、平文メールの際の描画を決められる。

`with` に配列を指定することで、bladeファイルに値を渡すことができる。
このテンプレート内部で使用する値は、コンストラクタの引数に渡しておこう。


### :pushpin: `attachments`

添付ファイルを指定するところ。
これは先の説明と同じ。




## 送信履歴について

送信履歴、送信トレイに、送信メールが保管されるかはメールサーバー次第。
基本的には履歴は残らないと思った方が良い。

送信時の戻り値をDBなどに格納しておこう。  
送信失敗時はちゃんと例外で処理される。