Laravel5.4 ユーザー登録時にメールアドレス認証を入れる

Authにはユーザー登録時のメールアドレス認証がないので入れてみる。

### .evn編集

デフォルトでsmtpになっているのでsendmailに変更しておく。

```Bash:.env
MAIL_DRIVER=sendmail

...

MAIL_FROM_ADDRESS=noreply@example.com #追加
MAIL_FROM_NAME=hoge #追加
```

### ユーザーテーブルに項目追加

```Bash:database/migrations/2014_10_12_000000_create_users_table.php
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->string('email_token')->nullable(); #追加
            $table->tinyInteger('verified')->default(0); #追加
            $table->tinyInteger('status')->default(0); #追加
            $table->timestamps();
        });
    }
```

再構築
`php artisan migrate:refresh`

### ユーザーモデルの編集

```Bash:app/User.php
    protected $fillable = [
        'name', 'email', 'password', 'email_token', 'verified'
    ];

    ...

    /**
     * メール認証が完了したら更新
     */
    public function verified()
    {
        $this->email_token = null;
        $this->verified = 1;
        $this->status = 1;
        $this->save();
    }
```

### メール認証クラスの作成

artisanコマンドで作成
`php artisan make:mail EmailVerification`

ユーザーモデルを追加する

```Bash:app/Mail/EmailVerification.php
<?php

namespace App\Mail;

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

class EmailVerification.php 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
     */
    public function build()
    {
        // メールのタイトルもここで変更
        return $this->subject('メールアドレスの登録確認')
                    ->view('email.verification');
    }
}
```

### メールビューの作成

```Php:resources/views/emails/verification.blade.php
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <style type="text/css" rel="stylesheet" media="all">
    /* Media Queries */
    @media only screen and (max-width: 500px) {
      .button {
        width: 100% !important;
      }
    }
  </style>
</head>
<?php
$style = [
  /* Layout ------------------------------ */
  'body' => 'margin: 0; padding: 0; width: 100%; background-color: #F2F4F6;',
  'email-wrapper' => 'width: 100%; margin: 0; padding: 0; background-color: #F2F4F6;',
  /* Masthead ----------------------- */
  'email-masthead' => 'padding: 25px 0; text-align: center;',
  'email-masthead_name' => 'font-size: 16px; font-weight: bold; color: #2F3133; text-decoration: none; text-shadow: 0 1px 0 white;',
  'email-body' => 'width: 100%; margin: 0; padding: 0; border-top: 1px solid #EDEFF2; border-bottom: 1px solid #EDEFF2; background-color: #FFF;',
  'email-body_inner' => 'width: auto; max-width: 570px; margin: 0 auto; padding: 0;',
  'email-body_cell' => 'padding: 35px;',
  'email-footer' => 'width: auto; max-width: 570px; margin: 0 auto; padding: 0; text-align: center;',
  'email-footer_cell' => 'color: #AEAEAE; padding: 35px; text-align: center;',
  /* Body ------------------------------ */
  'body_action' => 'width: 100%; margin: 30px auto; padding: 0; text-align: center;',
  'body_sub' => 'margin-top: 25px; padding-top: 25px; border-top: 1px solid #EDEFF2;',
  /* Type ------------------------------ */
  'anchor' => 'color: #3869D4;',
  'header-1' => 'margin-top: 0; color: #2F3133; font-size: 19px; font-weight: bold; text-align: left;',
  'paragraph' => 'margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;',
  'paragraph-sub' => 'margin-top: 0; color: #74787E; font-size: 12px; line-height: 1.5em;',
  'paragraph-center' => 'text-align: center;',
  /* Buttons ------------------------------ */
  'button' => 'display: block; display: inline-block; width: 200px; min-height: 20px; padding: 10px;
               background-color: #3869D4; border-radius: 3px; color: #ffffff; font-size: 15px; line-height: 25px;
               text-align: center; text-decoration: none; -webkit-text-size-adjust: none;',
  'button--green' => 'background-color: #22BC66;',
  'button--red' => 'background-color: #dc4d2f;',
  'button--blue' => 'background-color: #3869D4;',
];
?>

<?php $fontFamily = 'font-family: Arial, \'Helvetica Neue\', Helvetica, sans-serif;'; ?>

<body style="{{ $style['body'] }}">
  <table width="100%" cellpadding="0" cellspacing="0">
    <tr>
      <td style="{{ $style['email-wrapper'] }}" align="center">
        <table width="100%" cellpadding="0" cellspacing="0">
          <!-- Logo -->
          <tr>
            <td style="{{ $style['email-masthead'] }}">
              <a style="{{ $fontFamily }} {{ $style['email-masthead_name'] }}" href="{{ url('/') }}" target="_blank">
                {{ config('app.name') }}
              </a>
            </td>
          </tr>

          <!-- Email Body -->
          <tr>
            <td style="{{ $style['email-body'] }}" width="100%">
              <table style="{{ $style['email-body_inner'] }}" align="center" width="570" cellpadding="0" cellspacing="0">
                <tr>
                  <td style="{{ $fontFamily }} {{ $style['email-body_cell'] }}">
                    <!-- Greeting -->
                    <h1 style="{{ $style['header-1'] }}">
                      こんにちは、{{ $user->name }}さん
                    </h1>

                    <!-- Intro -->
                    <p style="{{ $style['paragraph'] }}">
                      以下のボタンをクリックして、メールアドレスの認証を行なってくださ>い。
                    </p>

                    <!-- Action Button -->
                    <table style="{{ $style['body_action'] }}" align="center" width="100%" cellpadding="0" cellspacing="0">
                      <tr>
                        <td align="center">
                          <?php
                            $actionColor = 'button--blue';
                          ?>

                          <a href="{{ url('register/verify/'.$user->email_token) }}"
                            style="{{ $fontFamily }} {{ $style['button'] }} {{ $style[$actionColor] }}"
                            class="button"
                            target="_blank">
                            認証する
                          </a>
                        </td>
                      </tr>
                    </table>

                    <!-- Outro -->
                    <p style="{{ $style['paragraph'] }}">
                      心当たりがない場合は、なにもせずにこのメールを削除してください。
                    </p>

                    <!-- Salutation -->
                    <p style="{{ $style['paragraph'] }}">
                      よろしくお願いします。
                    </p>
                  </td>
                </tr>
              </table>
            </td>
          </tr>

          <!-- Footer -->
          <tr>
            <td>
              <table style="{{ $style['email-footer'] }}" align="center" width="570" cellpadding="0" cellspacing="0">
                <tr>
                  <td style="{{ $fontFamily }} {{ $style['email-footer_cell'] }}">
                    <p style="{{ $style['paragraph-sub'] }}">
                      &copy; {{ date('Y') }}
                      <a style="{{ $style['anchor'] }}" href="{{ url('/') }}" target="_blank">{{ config('app.name') }}</a>.
                      All rights reserved.
                    </p>
                  </td>
                </tr>
              </table>
            </td>
          </tr>
        </table>
      </td>
    </tr>
  </table>
</body>
</html>
```

### Registerコントローラーの更新

```Php:app/Http/Controllers/Auth/RegisterController.php
<?php
...

use DB; //追加
use Mail; //追加
use Illuminate\Http\Request; //追加
use App\Mail\EmailVerification; //追加

...

    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
            'email_token' => str_random(10), //追加
        ]);
    }

　　　　　　　　// registerメソッドをオーバーライド
    public function register(Request $request)
    {
        // validation
        $validator = $this->validator($request->all());
        if ($validator->fails())
        {
            $this->throwValidationException($request, $validator);
        }

        // DBトランザクションを利用する
        DB::beginTransaction();
        try
        {
            $user = $this->create($request->all());
            
            // 名前とトークンはメールビューで利用しているのでパラメーターで渡す
            $email = new EmailVerification(new User(['name' => $user->name, 'email_token' => $user->email_token]));
            Mail::to($user->email)->send($email);
            DB::commit();

            $request->session()->flash('message', '入力したメールアドレス宛に「メールア
ドレスの登録確認」メールが届くので確認してね！');
            return redirect('login');
        }
        catch(Exception $e)
        {
            // 失敗したらロールバック
            DB::rollback();

            $request->session()->flash('message', 'エラー！');
            return redirect('login');
        }
    }

    // メールアドレスの認証
    public function verify($token)
    {
      User::where('email_token', $token)->firstOrFail()->verified();
      request()->session()->flash('message', 'メールアドレスの登録確認が終わったよ！');
      return redirect('login');
    }
```

### Registerビューにメッセージ表示

panel-bodyの後ろあたりに追加

```Bash:resources/views/auth/register.blade.php
@if(Session::has('message'))
    <div class="alert alert-info">{{Session::get('message')}}</div>
@endif
```

### ルーティング追加

Auth::routes()の後ろあたりに追加

```Bash:routes/web.php
Route::get('/register/verify/{token}', 'Auth\RegisterController@verify');
```

### Loginコントローラーの修正

```Bash

use Illuminate\Http\Request; //追加

...

    /**
     * credentialsメソッドをオーバーライド
     *   ログイン認証にstatusを追加する
     */
    protected function credentials(Request $request)
    {
      return [
        'email' => $request->email,
        'password' => $request->password,
        'status' => 1,
      ];
    }
```

### Loginビューにメッセージ表示

panel-bodyの後ろあたりに追加

```Bash:resources/views/auth/login.blade.php
@if(Session::has('message'))
    <div class="alert alert-info">{{Session::get('message')}}</div>
@endif
```