Laravel5.7: ユーザー登録確認用のメールを送信する

Laravel5.7から追加された新機能を使って、下記のようなユーザー登録の確認を促すメールを送信します。
登録したアドレス宛にメールを送信し、その中のリンクをユーザーがクリックすることで登録手続きを完了します。
:link: readouble.com: [メール確認](https://readouble.com/laravel/5.7/ja/verification.html)

![003.png](https://qiita-image-store.s3.amazonaws.com/0/27816/619c3981-d13a-facf-a29f-66ca521c0f11.png)

# 親記事
[Laravel 5.7で基本的なCRUDを作る - Qiita](https://qiita.com/sutara79/items/ef30fcdfb7afcb2188ea)


# Userモデルを修正

```diff:app/User.php
 <?php
 
 namespace App;
 
 use Illuminate\Notifications\Notifiable;
-use Illuminate\Contracts\Auth\MustVerifyEmail;
+use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract;
 use Illuminate\Foundation\Auth\User as Authenticatable;
 use App\Notifications\CustomPasswordReset;
+use Illuminate\Auth\MustVerifyEmail;
 
-class User extends Authenticatable
+class User extends Authenticatable implements MustVerifyEmailContract
 {
-    use Notifiable;
+    use MustVerifyEmail, Notifiable;
```

# DBのテーブルにカラムを追加
`users`テーブルに`email_verified_at`カラムを追加します。
しかし、Laravel5.7を新規インストールした場合は、マイグレーションのファイルに既に追加されています。
[以前の記事](https://qiita.com/sutara79/items/e19fb386451ab9a4a5fa#users%E7%94%A8%E3%81%AE%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3)をご覧ください。

```php:database/migrations/2014_10_12_000000_create_users_table.php
            $table->timestamp('email_verified_at')->nullable();
```

# メール確認の機能を有効にする
認証関連のルートを下記のように修正します。

```diff:routes/web.php
-Auth::routes();
+Auth::routes(['verify' => true]);
```

# ミドルウェア`verified`を利用する
この段階で本物のメールアドレスを使ってユーザー登録すると、下記のようなメールが届きます。

![001.png](https://qiita-image-store.s3.amazonaws.com/0/27816/4a88e338-d99d-2740-5e5a-396f16dbe173.png)


中央の水色のボタンをクリックすれば登録手続き完了となりますが、実は完了しなくても記事を投稿することができてしまいます。
これでは登録確認の意味がありません。
そこで、ミドルウェア`verified`を使います。
[以前の記事](https://qiita.com/sutara79/items/fdbfbf2a8f86f7d8f7fb#%E8%A8%98%E4%BA%8B%E3%81%AE%E6%8A%95%E7%A8%BF%E7%B7%A8%E9%9B%86%E5%89%8A%E9%99%A4%E3%81%AF%E8%AA%8D%E8%A8%BC%E3%82%92%E5%BF%85%E9%A0%88%E3%81%A8%E3%81%99%E3%82%8B)では、閲覧以外のアクション(投稿、編集、削除)はログイン必須とするためにミドルウェア`auth`を使っていました。
この`auth`の代わりに`verified`を使います。
具体的には、UserControllerとPostControllerの`__construct()`メソッドを下記のように修正します。
`auth`を使った記述も、コメントアウトして残しておきます。

```php:app/Http/Controllers/PostController.php
class PostController extends Controller
{
    /**
     * 各アクションの前に実行させるミドルウェア
     */
    public function __construct()
    {
        // $this->middleware('auth')->except(['index', 'show']);
        $this->middleware('verified')->except(['index', 'show']);
    }
```

```php:app/Http/Controllers/UserController.php
class UserController extends Controller
{
    /**
     * 各アクションの前に実行させるミドルウェア
     */
    public function __construct()
    {
        // $this->middleware('auth')->except(['index', 'show']);
        // 登録完了していなくても、退会だけはできるようにする
        $this->middleware('auth')->only('destroy');
        $this->middleware('verified')->except(['index', 'show', 'destroy']);
    }
```

これで、登録完了していない状態で記事を投稿しようとすると、下記のようにメール確認を促されるようになりました。

![002.png](https://qiita-image-store.s3.amazonaws.com/0/27816/0cf4cc70-a5c2-f2bb-c8d6-923a85480786.png)

また、メール確認が完了していない段階でも退会だけはできるように、UserControllerでは`verified`の除外リストに`destroy`を追加しています。
そのかわり、`destroy`の際には`auth`で認証状態を検査するようにしています。


# 独自のテンプレートで日本語のメールを送る
せっかくなので、[以前の記事](https://qiita.com/sutara79/items/0ea48847f5565aacceea)を応用して日本語のメールを送るようにします。

## 通知クラスを作る
下記を実行して`app/Notifications/CustomVerifyEmail.php`を生成します。

```:PowerShell
> php artisan make:notification CustomVerifyEmail
```

## Userモデルでメソッドを上書きする
Userモデルが利用している [Illuminate\Auth\MustVerifyEmail](https://github.com/laravel/framework/blob/5.7/src/Illuminate/Auth/MustVerifyEmail.php) トレイトで定義されている`sendEmailVerificationNotification()`メソッドを上書きします。

```php:app/User.php
// 忘れずにインポートすること
use App\Notifications\CustomVerifyEmail;

class User extends Authenticatable implements MustVerifyEmailContract
{
    (中略)

    /**
     * メール確認通知の送信
     *
     * @return void
     */
    public function sendEmailVerificationNotification()
    {
        $this->notify(new CustomVerifyEmail());
    }
```

## 通知クラスを編集する 
[Illuminate\Auth\Notifications\VerifyEmail](https://github.com/laravel/framework/blob/5.7/src/Illuminate/Auth/Notifications/VerifyEmail.php) クラスを参考にして、下記のように修正します。

```php:app/Notifications/CustomVerifyEmail.php
// 忘れずにインポートすること
use Illuminate\Auth\Notifications\VerifyEmail;

// 拡張元を Notification ではなく VerifyEmail とする。
// そうしなければ、後述の $this->verificationUrl() を使えない。
// class CustomVerifyEmail extends Notification
class CustomVerifyEmail extends VerifyEmail
{
    (中略)

    // toMail() メソッドの中身を下記のようにする
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->subject(__('Verify Your Email Address'))
            ->view('emails.verify')
            ->action(__('Verify Email Address'), $this->verificationUrl($notifiable));
    }
```

## メールのビューを作る
上の`->view('emails.verify')`という記述に合わせて、`verify.blade.php`を新規作成します。

```php:resources/views/emails/verify.blade.php
{{-- ユーザー登録確認用のメール --}}
<h3>
    <a href="{{ config('app.url') }}">{{ config('app.name') }}</a>
</h3>
<p>
    {{ __('Please click the link below to verify your email address.') }}<br>
    {{ __('If you did not create an account, no further action is required.') }}
</p>
<p>
    <a href="{{ $actionUrl }}">{{ $actionText }}</a>
</p>
```

なお、それぞれの文章の翻訳は、すでに [resources/lang/ja.json](https://github.com/sutara79/demo-laravel-crud/blob/5.7/resources/lang/ja.json) に記載しています。

## 動作確認
1. Laravelアプリの表示言語を日本語に切り替えます。
1. 本物のメールアドレスでユーザー登録します。
1. 下記のような日本語のメールが届けば成功です。

![003.png](https://qiita-image-store.s3.amazonaws.com/0/27816/619c3981-d13a-facf-a29f-66ca521c0f11.png)