LaravelとAmazon SES を使ってメールを送ろう！

## はじめに
タイトルの通り、とても簡単にLaravelとAmazon SESを使ってメールを送ることができます。
記事自体は長いのですが作業自体は特に難しくありません。
Laravel使ってメールを送りたいと考えている方（そうでない方も笑）、ぜひ記事に目を通していってください！

**著者の環境**

- Laravel 8.78.1
- PHP 8.0.8

## 本記事の概要
#### ゴール
アプリから以下のようなメールが送信できることを目標にしていきます。

|項目|説明|
|:--|:--|
|from|mail@test-ses.tk|
|fromの表示名|アプリ名|
|to|アプリにて指定した宛先|

#### やること
メールを送信するために以下のステップを踏んでいきます。
もしご自身にとって不要なステップがあったら飛ばしてください。

- [IAMユーザーの作成](#iamユーザーの作成)
- [SESの設定](#sesの設定)
    - 独自ドメインの取得
    - SESの設定
    - サンドボックス外への移動
- [Laravelの設定](#laravelの設定)

## IAMユーザーの作成
LaravelからSESにメールの送信リクエストをするには、認証のためにIAMユーザーが必要です。
（最終的に`.env`にIAMユーザーのアクセスキーやシークレットアクセスキーを記入します）

そのため、まずはIAMユーザーを作成しましょう。
今回は以下のようなポリシーを作成し、このポリシーを直接IAMユーザーにアタッチしました（直接IAMユーザーにポリシーアタッチするのは良くないのですが、テストなので許してください笑）

```json:作成したポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        }
    ]
}
```

**【補足】** アクセスキーとシークレットアクセスキーを確認する方法
>IAMユーザーのユーザーページから認証情報タブをクリックします。
`アクセスキーの作成`をクリックすると、アクセスキーとシークレットアクセスキーが確認できます。

## SESの設定
SESを利用するには、送信するためのドメイン又はメールアドレスが必要になります。
今回は送信元にドメインを指定します。
以下独自ドメインの取得についても記載していますが、取得済みの方は読み飛ばしてください。

#### 独自ドメインの取得
今回は[freeenomというサービス](https://my.freenom.com/clientarea.php)を用いて無料でドメインを取得しました。

- freenomへの登録を済ませたらヘッダーにある`Services`内の`Register New Domain`をクリックします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/65f5d25e-a7a1-9904-3bd0-d9f06c6341d9.jpeg" width="20%">

- 好きなドメインのところで`Get it now`を押します。そうすると、`selected`に表示が切り替わります（今回はtest-ses.tkを選択しました）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/145c2304-118c-3163-2e0a-823b962e5357.jpeg" width="60%">

- その後は完了画面になるまで、continue等を押していきます（完了画面は注文番号が出てくる画面です)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/0fda5bf7-f18c-25cc-4b71-19b851063d27.jpeg" width="60%">
※ 途中にドメインを取得する期間を設定する場所があるので、その部分はご自身の希望の期間を設定してください。デフォルトは3ヶ月です。

これで独自ドメインの取得は以上になります。
しかしこのままではSESで色々ドメインの設定をした時に、freenomで管理をしなければならず結構面倒です。
そのためRoute53に管理を委任していきます。

- まずはRoute53にて、先ほど取得した独自ドメインのホストゾーンを作成します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/2ef5b9ee-626a-3c58-c690-9d6c24eba240.jpeg" width="60%">

- そうしたら4つのNSレコードが作成されます（SOAレコードも作成されますが、今回はNSレコードのみ使用します）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/c9481285-ddfd-438a-7aad-f14109816040.jpeg" width="60%">

それではfreenomeにてRoute53のNSレコードを登録していきましょう。

- freenomのヘッダーにある`Services`内の`My Domains`をクリックします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/108df0a1-b14e-583b-4317-e983656c2585.jpeg" width="20%">

- その後、自身のドメインの`Manage Domain`というボタンを押下し、出てきた画面の`Nameservers`をクリックします
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/972ec185-5bc6-719d-fcc0-0a4a78f05e36.jpeg" width="50%">

- 先ほどRoute53のNSレコードに記載してあったものを記入していきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/406cecf1-ee33-0321-edff-b9fea81390b1.jpeg" width="50%">

- 完了したらdigコマンドにてroute53のNSレコードが出てくるか確認しましょう。
※直ぐには反映されないこともあります。

```bash
$ dig NS test-ses.tk +short
ns-1347.awsdns-40.org.
ns-14.awsdns-01.com.
ns-1629.awsdns-11.co.uk.
ns-823.awsdns-38.net.
```

これで独自ドメインの設定は完了です。
それではSESの設定をしていきましょう。

#### SESの設定
- SESのコンソール画面にいったら`Create identity`というボタンを押します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/6f3f5b58-957b-8bf3-94d5-b9bc14d8d0f0.jpeg" width="60%">


- まずは、送信するドメインを設定します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/c89198cd-1a96-d32e-de2e-24b1071f5e64.jpeg" width="60%">

- 次にDKIMの設定をしていきます。今回はSESにて簡単に設定できる`EasyDKIM`を選択しました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/f100d0ce-9c75-1a89-db8e-0b351cf7f794.jpeg" width="60%">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/bf2a69f6-b6d9-187b-1266-b47ab6cacee3.jpeg" width="60%">

- Route53を見ると、設定したDKIMのCNAMEレコードが追記されています（画像の3つのレコード全てがDKIMのドメインです）。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/ea8e167a-4f0b-34e3-064d-31bd3e0bbb1c.jpeg" width="70%">

**【補足】**DKIM（DomainKeys Identified Mail）について
>メールの電子署名を用いた不正防止の仕組みです。送信者はメールに電子署名を行います。メール受信者がドメインが正式なものかを問い合わせて不正を防止しています。

#### サンドボックス外への移動
SESはデフォルトの状態で Amazon SES サンドボックスに配置されます。
これにより以下のような制限がかかってしまいます。

- SESに登録したメールアドレス（ドメイン）からしかメールを送信することができない
- 最大で 24 時間あたり 200 メッセージ
- 最大で 1 秒あたり 1 メッセージを送信できます

どのアドレスへもメールを問題なく送信したい場合、Amazon SES サンドボックスから削除されるようにリクエストする必要があります。

- SESのTopページから`Request production access`というボタンを押します
![スクリーンショット 2022-01-13 9.01.13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/effa0823-4968-0195-59e4-0e36ab2f3b92.jpeg)

- 各種必要な情報を入力して、Requestを送信します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/799854/cda5841a-0242-1c73-f944-b483185f154e.jpeg" width="60%">

これでSESの設定は完了です。
次にLaravelの方の設定をしていきましょう。

## Laravelの設定
#### 各種準備
- まずはライブラリをインストールします。

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

- 次に.envファイルを編集していきます。
使用するメーラーやIAMの情報を記入していきましょう。

```bash:.env
MAIL_MAILER=ses
MAIL_FROM_ADDRESS=mail@test-ses.tk # @より前は好きな文字を記入し、@の後はSESにて設定したドメインを設定します。
MAIL_FROM_NAME="${APP_NAME}" #FROMの表示名をアプリ名以外にしたい場合は、書き換えてください。

# 作成したIAMユーザーのアクセスキーを記入する
AWS_ACCESS_KEY_ID=この部分はIAMのアクセスキーを記入してください。
AWS_SECRET_ACCESS_KEY=この部分はIAMのシークレットアクセスキーを記入してください
AWS_DEFAULT_REGION=ap-northeast-1 #東京リージョンではない場合は書き換えてください。
```

- config/mail.phpの変更<br>
※ 基本的に.envの設定値が適用されるので、第2引数は書き換えなくても動作しますが念の為設定しておきます。

```php:config/mail.php
'default' => env('MAIL_MAILER', 'ses'),

'from' => [
    'address' => env('MAIL_FROM_ADDRESS', 'hiroaki-u@hiroaki-u.tk'),
    'name' => env('MAIL_FROM_NAME', 'Test'),
],
```

- config/services.php<br>
※ 基本的に.envの設定値が適用されるので、第2引数は書き換えなくても動作しますが念の為設定しておきます。

```php:config/services.php
return [
    // 中略
    'ses' => [
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION', 'ap-northeast-1'),
    ],

];
```

#### 実装
LaravelではMailファサードを使ってMailを送ることが出来ます。
Mailファサードのみでも送信することはできるのですが、メールの文面等はMailableクラスを用いると楽に実装できるので、今回はMailableクラスを用いようと思います。

- Mailableの生成

```bash
$ php artisan make:mail MailTest
```
上記のartisanコマンドを打つと、app/Mail/MailTest.phpが作成されます。
メールの文面にDBのデータが必要な場合は、メンバー変数にModelを持ってきて、コンストラクタにModelをDIする必要があります。
今回はUserのデータを使用すると仮定します。

```php:MailTest.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

use App\Models\User;

class MailTest extends Mailable
{
    use Queueable, SerializesModels;

    public $user;
    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    // subjectメソッドでメールのタイトルを設定
    // viewメソッドでメールの本文が書いてあるbladeを指定
    public function build()
    {
        return $this
        ->subject('メールの題名')
        ->view('emails.test_content');
    }
}
```

- メールの内容を実装

resources/views内にemails/test_content.blade.phpを作成します。

```bash
$ mkdir -p resources/views/emails && touch ./resources/views/emails/test_content.blade.php
```

メールの文面を書いていきます。
今回は『[ユーザー名]さんへのメール』という文面を記載しています。

```php:test_content.blade.php
<body>
  <p>{{ $user->name }}さんへのメール</p>
</body>
```

- メールの送信処理の実装<br>
Mailファサードを用いてtoメソッドにより送信先を設定し、sendメソッドによりMailableのインスタンスを引数にします。
この処理でメールを送ることが出来ます。

```php:TestController.php
use App\Mail\MailTest;
use Illuminate\Support\Facades\Mail;

class TestController extends Controller
{
    public function index()
    {
        $mail_address = [自分が送りたい宛先];
        Mail::to($mail_address)->send(new MailTest($review,$user));
    }
}
```

これで以上となります！
最後まで読んでくださりありがとうございました。

## 参考記事
- [Laravelメール送信（公式）](https://readouble.com/laravel/8.x/ja/mail.html)
- [SESの送信概要（公式）](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-email-concepts-process.html)
- [Amazon SES でメール送信するときのベストプラクティスまとめ](https://dev.classmethod.jp/articles/ses-send-mail-best-practices-summary/)
- [SESのIAMポリシー設定](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/control-user-access.html)
- [SESのサンドボックス制限](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/request-production-access.html)