はじめに
こちらのめちゃくちゃ丁寧に書いてくださっているQiitaの記事を元に作成しています。
ですが初心者の私には今どこをやっているんだろうと、すんなりといかなかったので完成画像や構成を紹介しながら分かりやすくしてみました。
この記事で分かりにくかった方には、参考元のQiitaの記事には筆者さまのソースも載っています。
機能
1.仮会員登録(メールアドレス / パスワードのみ)
2.メール送信(本登録用のURL記載)
3.本登録
1仮会員登録
完成イメージ(*画像)
①仮登録フォーム 画面 pre_register.blade.php
②仮登録内容 確認画面 pre_register_check.blade.php
③仮登録完了 画面 pre_registered.blade.php
仮登録の手順
①各画面を作成(3つ)
②usersテーブルにメール認証用のカラム追加
③RegisterControllerに仮登録 register() と pre_check() を作成・変更 & we.phpにルート追加
追加 / 変更する点
views(authディレクトリー)
・仮登録フォーム 画面 pre_register.blade.php
・仮登録内容 確認画面 pre_register_check.blade.php
・仮登録完了 画面 pre_registered.blade.php
users_table
・users_tableにカラム追加
・users_tableのnameカラムをnullableに変更
RegisterController
・pre_check() 仮登録内容 確認内容に出す内容について記載
・register() もともとlaravelのauthに入っているregisterを変更
User.php
$fillableに追加したカラムを追加(email_token , email_varified)
仮登録
Laravelの認証機能を利用
php artisan make:auth
User_tableにカラムを追加
php artisan make:migration add_votes_to_users_table --table=users
class AddColumnsUsersTable extends Migration { public function up() { Schema::table('users', function (Blueprint $table) { $table->boolean('email_verified')->default(0); $table->string('email_verify_token')->nullable(); $table->string('phone_number')->nullable(); $table->dateTime('birthday')->nullable(); $table->dateTime('address')->nullable(); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('email_verified'); $table->dropColumn('email_verify_token'); $table->>dropColumn('phone_number'); $table->>dropColumn('birthday'); $table->>dropColumn('address'); }); }
booleanはemail_varified(メール認証)がされていれば1、されていなければ0
email_verify_tokenはYkBnbWFpbC5jb20=のようなもの
このままではname欄が必須のままでエラーとなるのでnullでも大丈夫なように変更します
php artisan make:migration change_column_users_table --table=users
$table->string(‘name’)->nullable(); としてください
php artisan migrate
仮登録フォーム画面作成 pre_register.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Register') }}</div> <div class="card-body"> <form method="POST" action="{{ route('register.pre_check') }}"> @csrf <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email"> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label> <div class="col-md-6"> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password"> </div> </div> <div class="form-group row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Register') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
新たに作成、名前をpre_しなくても、php artisan make:auth で作成されたview/auth/register.blade.phpと同じです。もしわかりやすいように名前をpre_で揃えるのであれば、RegistersUsersのshowRegistrationForm()でreturnされるview名も変更
仮登録は「メールアドレス」「パスワード」のみの登録なので、pre_register.blade.php のnameの記載を削除
- <div class="form-group row"> - <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label> - - <div class="col-md-6"> - <input id="name" type="text" class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" value="{{ old('name') }}" required autofocus> - - @if ($errors->has('name')) - <span class="invalid-feedback"> - <strong>{{ $errors->first('name') }}</strong> - </span> - @endif - </div> - </div>
今回は仮登録内容 確認画面 を出すのでpre_register.blade.php のformの送信先を変更
- <form method="POST" action="{{ route('register') }}"> + <form method="POST" action="{{ route('register.pre_check') }}">
web.phpに追加
Route::post('register/pre_check', 'Auth\RegisterController@pre_check')->name('register.pre_check');
RegisterController変更
「名前」を削除したのでValidator()からnameを削除
protected function validator(array $data) { return Validator::make($data, [ - 'name' => 'required|string|max:255', 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6|confirmed', ]); }
先程web.phpに記載したpre_check()をRegisterControllerに記載
public function pre_check(Request $request){ $this->validator($request->all())->validate(); $bridge_request = $request->all(); // password マスキング $bridge_request['password_mask'] = '******'; return view('auth.register_check')->with($bridge_request); }
User.php
protected $fillable = [ 'name', 'email', 'password', 'email_verified', 'email_verify_token','address', 'phone_number', 'birthday' ];
いったん仮登録フォーム完成
2メール
完成イメージ(*画像)
mailtrapに本登録URL付きのメールが届きます
メール送信の手順
①mailtrapアカウントを作成(2、3分で無料でできます)
②.envファイル変更
③EmailVerification.php作成 & 変更
④user_register_plain (メール本文)を作成
⑤RegisterControllerの create() を変更
追加 / 変更する点
views(authディレクトリー)
・メールテンプレート user_register_plain.blade.php
.env
mailtrap利用の為、変更
実際のメールアドレスでテストするのは大変なのでmailtrapというwebサービスを利用します。
EmailVerification.php
・__construct()
・build() メールの内容や件名を指定
RegisterController
・create()変更 実際にここでメールを送る指示を出す
メール送信
mailtrapアカウントを作成
2~3分で簡単に作成できます。テストデータの中に間違って本番ユーザのメールアドレスがあった場合には、取り返しのつかない事故が発生してしまう可能性もあります。そんな危険を回避しつつテストしやすくしてくれるのがmailtrapというWebサービスです。SMTPでメールを送信しても実際の宛先には飛ばさず、WebサイトやAPIから確認することができるというサービスです。こちらの記事を参考にして作成してください。
.envファイルの変更
MAIL_DRIVER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME= (黒塗りの該当する箇所) MAIL_PASSWORD= (黒塗りの該当する箇所) MAIL_ENCRYPTION = tls + MAIL_FROM_ADDRESS=from@example.com(メール送信時のデフォルト値設定可能) + MAIL_FROM_NAME=LaravelExampler(メール送信時のデフォルト値設定可能)
認証メールに使用するviewと、tokenを返すクラスを作成
php artisan make:mail EmailVerification
このコマンドで、app/MailにMailableを継承したEmailVerificationクラスが作成されます。
このクラスにメール送信に使用するviewやメールタイトルなどの設定を記述することになります。
EmailVerification.php
EmailVerification.php が作成され、その中に __construct() と build() があります。
__construct() にユーザー情報を渡すために変数を定義
<?php namespace App\Mail; use ... class EmailVerification extends Mailable { use Queueable, SerializesModels; protected $user; public function __construct($user) { $this->user = $user; }
build()でメールを送る際の件名やviewを指定します。
public function build() { return $this ->subject('【activity】登録手続きのご案内') ->view('emails.user_register_plain') ->with(['token' => $this->user->email_verify_token, ]); }
user_register_plain.blade.php (メール本文)を作成
今回emailsディレクトリを作成してその中に入れてますが、いい感じの名前に変更してください
サイトへのアカウント仮登録が完了しました。<br> <br> 以下のURLからログインして、本登録を完了させてください。<br> {{url('register/verify/'.$token)}}
RegisterController
pre_register_check.blade.php でフォームの送信先となっている register は RegisterController で use しているRegistersUsersにあります
public function register(Request $request) { event(new Registered($user = $this->create( $request->all() ))); return view('auth.pre_registered'); }
ここで実際にDBに作成されるのは、$user = $this->create( $request->all() )) で RegisterController の create() です。
ここでusersDBに入れられ、メール送信の指示を追加します。
protected function create(array $data) { $user = User::create([ 'email' => $data['email'], 'password' => Hash::make($data['password']), 'email_verify_token' => base64_encode($data['email']), ]); $email = new EmailVerification($user); Mail::to($user->email) ->send($email); return $user; }
3本登録
完成イメージ(*画像)
①本登録フォーム 画面 main_register.blade.php
②本登録内容 確認画面 main_register.blade.php
③本登録 完了画面 main_register.blade.php
本登録の手順
①UserForm.php 作成
②各画面を作成(3つ)
③RegisterControllerに本登録 showForm() と mainCheck() とmainRegister() を作成・変更 & web.phpにルート追加
④User.phpを変更
追加 / 変更する点
views(authディレクトリー)
・本登録フォーム 画面 main_register.blade.php
・本登録内容 確認画面 main_register_check.blade.php
・本登録完了 画面 main_registered.blade.php
RegisterController
・showForm() 本登録フォームを出す
・mainCheck() 本登録内容 確認画面を出す
・mainRegister() フォーム送信 DB登録
User.php
$fillableに氏名、電話番号その他保存したい情報を追加
UserForm
※参考元の登録フォームにはlaravelのformは使用していないので、使用しない方はそちらを参考にしてください。
Rules/Birthday.php
存在しない日付をエラーで返す
config/const.php
statusを記載
本登録
FormsディレクトリとUserForm.phpを作成
php artisan make:form Forms/UserForm
composer.jsonにlaravel-form-builderを追加
"require": { "php": "^7.1.3", "aws/aws-sdk-php": "~3.0", "doctrine/dbal": "^2.9", "fideloper/proxy": "^4.0", "kris/laravel-form-builder": "dev-master", }
ターミナルでcomposer update
composer update
config/app.phpにlaravel-form-builderを追加
'providers' => [ : : Kris\LaravelFormBuilder\FormBuilderServiceProvider::class, ]; 'aliases' => [ : : 'formBuilder' => 'Kris\LaravelFormBuilder\Facades\FormBuilder', ];
UserForm.php
<?php namespace App\Forms; use Kris\LaravelFormBuilder\Form; use App\Rules\Birthday; use App\User; $BIRTH_MONTH_CHOICES = []; foreach (range(1, 12) as $month) { $BIRTH_MONTH_CHOICES[$month] = $month; } $BIRTH_DAY_CHOICES = []; foreach (range(1, 31) as $day) { $BIRTH_DAY_CHOICES[$day] = $day; } define('BIRTH_MONTH_CHOICES', $BIRTH_MONTH_CHOICES); define('BIRTH_DAY_CHOICES', $BIRTH_DAY_CHOICES); class UserForm extends Form { public function buildForm() { $this ->add('name','text',[ 'rules' => 'required', 'label' => '名前' ]) ->add('address','text',[ 'rules' => 'required', 'label' => '住所' ]) ->add('birth_month','select',[ 'rules' => 'required', 'attr' => ['class' => 'form-control rounded-pill'], 'choices' => BIRTH_MONTH_CHOICES, 'empty_value' => '--' ]) ->add('birth_day','select',[ 'rules' => 'required', 'attr' => ['class' => 'form-control rounded-pill'], 'choices' => BIRTH_DAY_CHOICES, 'empty_value' => '--' ]); $this->addBirthYear(); $this->addPhoneNumber(); } public function addBirthYear() { $currentYear = date('Y'); $birthYearChoices = []; foreach (range($currentYear, $currentYear - 100) as $year) { $birthYearChoices[$year] = $year; } $this ->add('birth_year', 'select', [ 'attr' => ['class' => 'form-control rounded-pill'], 'rules' => ['required', new Birthday($this->request->all())], 'choices' => $birthYearChoices, 'empty_value' => '----', ]); } public function addPhoneNumber() { $placeholder = ['090', '1234', '5678']; for ($i = 1; $i <= 3; $i += 1) { $this ->add("phone_number$i", 'number',[ 'attr' => [ 'class' => 'form-control rounded-pill', 'placeholder' => $placeholder[$i - 1] ], 'rules' => 'required', ]); } } public function getFieldValues($with_nulls = true) { $values = parent::getFieldValues($with_nulls); $birthdayValues = []; foreach (['year', 'month', 'day'] as $birth) { $birthdayValues[] = $values["birth_$birth"]; } $values['birthday'] = sprintf("%04d-%02d-%02d", ...$birthdayValues); $phoneNumberValues = []; for ($i = 1; $i <= 3; $i += 1) { $phoneNumberValues[] = $values["phone_number$i"]; } $values['phone_number'] = join('-', $phoneNumberValues); for ($i = 1; $i <= 3; $i += 1) { unset($values["phone_number$i"]); } return $values; } public function check() { $values = $this->getFieldValues(); $user = new User(); $user->name = $values['name']; $user->address = $values['address']; $user->phone_number = $values['phone_number']; $user->birthday = $values['birthday']; return $user; }
誕生日はuserの入力画面ではbirth_year , month , dayと3つに分けていますが、DBに登録のときはbirthdayと一つのカラムとして登録するようにgetFieldValues()で整えています。
addBirthYear()では毎年新しい年が自動で追加されるよう、currentYear(今年)から100年前までを選択表示させるようにしています。
addPhoneNumber()では元から一つの入力フォームにしても大丈夫ですが、今回は分けています。
Rules/Birthday.php 作成
Rulesディレクトリを作成し、Birthday.phpを作成します。ここでは存在しない日付が選択されないよう、チェックします。(2月31日など)
<?php namespace App\Rules; use Illuminate\Contracts\Validation\Rule; class Birthday implements Rule { private $values; /** * Create a new rule instance. * * @return void */ public function __construct($values) { $this->values = $values; } /** * Determine if the validation rule passes. * * @param string $attribute * @param mixed $value * @return bool */ public function passes($attribute, $value) { $birthdayValues = []; foreach (['month', 'day', 'year'] as $suffix) { $birthdayValues[] = $this->values["birth_$suffix"]; } return checkdate(...$birthdayValues); } /** * Get the validation error message. * * @return string */ public function message() { return '生年月日に誤りがあります'; } }
本登録フォーム main_register.blade.phpを作成
@extends('layouts.app') @section('content') <div class="container"> <div class="row d-flex"> <div class="col-md-8 justify-content-center"> <div class="card"> <div class="card-header">本会員登録</div> @isset($message) <div class="card-body"> {{$message}} </div> @endisset @empty($message) <div class="card-body"> <form action="{{ route('register.main_pre_check', ['token' => $email_token]) }}"> @csrf <div class="row"> <div class="col-sm-6"> <div class="form-group"> <label>{!! form_label($form->name) !!}</label> {!! form_widget($form->name) !!} </div> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>生年月日</label> <div class="row"> <div class="col"> {!! form_widget($form->birth_year) !!} </div> <div class="m-auto"> / </div> <div class="col"> {!! form_widget($form->birth_month) !!} </div> <div class="m-auto"> / </div> <div class="col"> {!! form_widget($form->birth_day) !!} </div> </div> <span class="error">{!! form_errors($form->birth_year) !!}</span> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>電話番号</label> <div class="row"> <div class="col"> {!! form_widget($form->phone_number1) !!} </div> <div class="m-auto"> - </div> <div class="col"> {!! form_widget($form->phone_number2) !!} </div> <div class="m-auto"> - </div> <div class="col"> {!! form_widget($form->phone_number3) !!} </div> </div> </div> </div> <div class="row"> <div class="col-sm-6"> <div class="form-group"> <label>{!! form_label($form->address) !!}</label> {!! form_widget($form->address) !!} </div> </div> </div> <div class="form-group row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> 確認画面へ </button> </div> </div> </form> </div> @endempty </div> </div> </div> </div> @endsection
web.php
メールアドレスに記載の本会員登録用URLがクリックされると、作成したmain_register.blade.php に遷移されるように設定(register/verify/{token})
Route::get('register/verify/{token}', 'Auth\RegisterController@showForm');
RegisterController に showForm() を追加
①DBに$email_tokenがなければエラーを出す
②もしstatusが ‘登録’ になっていたら既に本登録されてる通知を出す
public function showForm($email_token) { $form = $this->form(UserForm::class); if ( !User::where('email_verify_token', $email_token)->exists()){ return view('auth.main_register', compact('form'))->with('message', '無効なトークンです。'); } else { $user = User::where('email_verify_token', $email_token)->first(); if ($user->status == config('const.USER_STATUS.REGISTER')) { return view('auth.main_register', compact('form'))->with('message', 'すでに本登録されています。ログインして利用してください。'); } $user->status = config('const.USER_STATUS.MAIL_AUTHED'); $user->email_verified_at = Carbon::now(); if ($user->save()) { return view('auth.main_register', compact('email_token','form')); } else { return view('auth.main_register', compact('form'))->with('message', 'メール認証に失敗しました。再度、メールからリンクをクリックしてください。'); } } }
いったん本登録フォーム完成
users_tableにstatusカラムを追加
php artisan make:migration add_column_users_table --table=users
class AddColumnUsersTable extends Migration { public function up() { Schema::table('users', function (Blueprint $table) { $table->tinyInteger('status')->default(0); }); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('status'); }); } }
php artisan migrate
config/const.phpを作成
ステータス値をconst(定数)で管理するようにしています。
<?php return [ /* |-------------------------------------------------------------------------- | Const |-------------------------------------------------------------------------- */ // 0:仮登録 1:本登録 2:メール認証済 9:退会済 'USER_STATUS' => ['PRE_REGISTER' => '0', 'REGISTER' => '1', 'MAIL_AUTHED' => '2', 'DEACTIVE' => '9',], ];
本登録内容 確認画面 main_register_check.blade.php作成
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">本会員登録確認</div> <div class="card-body"> <form method="POST" action="{{ route('register.main.registered', ['token' => $email_token]) }}"> @csrf <div class="form-group row"> <label for="name" class="col-md-4 col-form-label text-md-right">名前</label> <div class="col-md-6"> <span class="">{{$user->name}}</span> <input type="hidden" name="name" value="{{$user->name}}"> </div> </div> <div class="form-group row"> <label for="phone_number" class="col-md-4 col-form-label text-md-right">電話番号</label> <div class="col-md-6"> <span class="">{{$user->phone_number}}</span> <input type="hidden" name="phone_number" value="{{$user->phone_number}}"> </div> </div> <div class="form-group row"> <label for="birthday" class="col-md-4 col-form-label text-md-right">生年月日</label> <div class="col-md-6"> <input type="hidden" name="birthday" value="{{$user->birthday}}"> {{ $user->birthday }} </div> </div> <div class="form-group row"> <label for="address" class="col-md-4 col-form-label text-md-right">住所</label> <div class="col-md-6"> <input type="hidden" name="address" value="{{$user->address}}"> {{ $user->address }} </div> </div> {!! form_errors($form->address) !!} <div class="form-group row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> 本登録 </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
本登録完了 画面 main_registered.blade.php 作成
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">本会員登録完了</div> <div class="card-body"> <p>本会員登録が完了しました。</p> <a href="{{url('/')}}" class="sg-btn">トップページへ戻る</a> </div> </div> </div> </div> </div> @endsection
RegisterController に 確認画面 mainCheck() と 本登録 mainRegister() を追加
public function mainCheck($email_token) { $form = $this->form(UserForm::class); if (!$form->isValid()) { return redirect()->back()->withErrors($form->getErrors())->withInput(); } $user = $form->check(); return view('auth.main_register_check', compact('user' , 'form', 'email_token')); } public function mainRegister(Request $request, $email_token) { $user = User::where('email_verify_token', $email_token)->first(); $user->name = $request->name; $user->phone_number = $request->phone_number; $user->birthday = $request->birthday; $user->address = $request->address; $user->save(); return view('auth.main_registered'); }
web.php
本登録確認画面、本登録のルートを登録
Route::get('register/main_check/{token}', 'Auth\RegisterController@mainCheck')->name('register.main_pre_check'); Route::post('register/main_register/{token}', 'Auth\RegisterController@mainRegister')->name('register.main.registered');
以上でメール認証ができたかと思います。