環境の構築が完了したので、実際に機能を置き換えていきます。まずはユーザー登録を対応させます。
(2016/11/8 エラーを変数でviewに渡していたのを、withErrorに変更)
(2016/12/21 HTMLのidをケバブケースに修正)
(2016/12/21 テストにwantToを追加)
方針
Sentinelのユーザー登録についての公式ドキュメントはこちら。
ここでは次のことをします。
- ユーザー名と、メールアドレスと、パスワードと、確認用のパスワードを受け付ける
- ユーザー登録を完了するには、メールによる確認作業を必要にする
- ユーザー名、メールアドレス、パスワードは必須
- パスワードと確認用パスワードは一致していなければならない
ユーザー登録のテストを作成
ユーザー登録処理をSentinelに組み替えるにあたり、事前にテストコードを作成しておきます。
ボタンを確実に押すために、フォームにIDを設定します。
- resources/views/auth/register.blade.php をエディターで開く
- 「<form」から始まる行を見つけて、以下のようにID属性を追加する(idをケバブケースに修正 2016/12/21)
<form id="register-user-form" class="form-horizontal" role="form" method="POST" action="{{ url('/register') }}">
続いて、テスト用のCestファイルを作成しいます。
- ターミナルから以下を実行して、ユーザー登録用のテストコードを生成
composer exec codecept g:cest functional RegisterUser
- tests/functional/RegisterUserCest.php が出来上がるので、エディターで開く
- tryToTestメソッドを以下のようにする(submitFormのidをケバブケースに修正 2016/12/21)
// tests public function tryToTest(FunctionalTester $I) { // 空欄での登録試みが失敗するのを確認 $I->wantTo(' 空欄での登録試みが失敗するのを確認.'); $I->amOnPage('/register'); $I->submitForm('#register-user-form', [ ]); $I->seeInCurrentUrl('register'); $I->see('name', '.help-block'); $I->see('email', '.help-block'); $I->see('password', '.help-block'); // パスワードの不一致を確認 $I->wantTo(' パスワードの不一致を確認.'); $I->amOnPage('/register'); $I->submitForm('#register-user-form', [ 'name' => 'テスト名前2', 'email' => 'test2@test.com', 'password' => 'testpass', 'password_confirmation' => 'not' ]); $I->seeInCurrentUrl('register'); $I->see('password', '.help-block'); // 登録成功 $I->wantTo(' ユーザーの登録成功を確認.'); $I->amOnPage('/register'); $I->submitForm('#register-user-form', [ 'name' => 'テスト名前', 'email' => 'test@test.com', 'password' => 'testpass', 'password_confirmation' => 'testpass' ]); $I->seeInCurrentUrl('login'); $I->see(trans('sentinel.after_register')); }
以下をテストするコードです。
- 空欄での登録試みが失敗するのを確認
- パスワードの不一致を確認
- 登録成功
npm test で実行すると、エラーが発生します。まだ、ユーザー登録処理を Sentinel に入れ替えてなく、アサーションするための文字列も用意していません。このテストコードを通すことを目指して作業を進めていきます。
テストに利用できるメソッドなどはこちら → Laravel5 - Codeception - Documentation
メールのテスト環境を構築
メール送信のテスト時に、実際に本当のメールアドレスに送信すると削除するなどが面倒です。そこで、テスト用のメールサービス Mailtrap.io — Fake smtp testing server. Dummy smtp email testing を利用することにします。
- Mailtrap.io — Fake smtp testing server. Dummy smtp email testing を開く
- はじめて利用する場合は、 [Sign up]から登録する。Google+やGitHubのアカウントがあれば、それで利用できる
- 登録したら[Log in]する
- [Demo Inbox]をクリックして、情報を表示
- .env.testing をエディターで開く
- 以下の項目を設定する
MAIL_DRIVER=smtp MAIL_HOST=mailtrap.io MAIL_PORT=25 MAIL_USERNAME=<MailtrapのUsername> MAIL_PASSWORD=<MailtrapのPassword> MAIL_ENCRYPTION=null
- 上書き保存する
以上で設定完了です。テストを実行した時のメールは、実際のメールアドレスではなく、 Mailtrap.io に届きますので、そちらで確認します。送信先に架空のメールアドレスが使えるのも便利です。
ユーザー登録コントローラーの作成
ユーザーを登録処理するコントローラーを作成します。アクティベーションが必要となるので、Laravel が事前に提供している処理は利用できません。独自のものに差し替えます。
- app/Http/Controllers/Auth/RegisterController.php をエディターで開いて、既存のコードを削除して、以下のものに差し替える
<?php namespace App\Http\Controllers\Auth; use Activation; use Mail; use Sentinel; use App\User; use App\Http\Controllers\Controller; use App\Notifications\RegisterNotify; use Illuminate\Support\Facades\Validator; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Http\Request; class RegisterController extends Controller { /** * Where to redirect users after login / registration. * * @var string */ protected $redirectTo = 'login'; /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest'); } /** * ユーザー登録 */ protected function register(Request $request) { $this->validate($request, [ // nameは必須で、255文字まで 'name' => 'required|max:255', // emailは必須で、emailの形式で、255文字までで、usersテーブル内でユニーク 'email' => 'required|email|max:255|unique:users', // passwordは必須で、6文字以上255文字以下で、確認欄と一致する必要がある 'password' => 'required|between:6,255|confirmed', ]); // 情報に問題がなければ、ユーザー登録 $credentials = [ 'name' => $request['name'], 'email' => $request['email'], 'password' => $request['password'], ]; $user = Sentinel::register($credentials); // アクティベーションを作成する $activation = Activation::create($user); // メールで送信する $usermodel = User::where('email', $user->email)->get()[0]; $usermodel->notify(new RegisterNotify($activation->code)); // メールを確認して、承認してからログインすることを表示するページへ return redirect($this->redirectTo)->with('info', trans('sentinel.after_register')); } }
- 上書き保存
コード内にある通知のための RegisterNotify クラスと、テスト用のメッセージがまだありません。テストコードを通すために、これらを用意します。
メッセージを作成
メッセージをハードコーディングするのを避けるために、言語ファイルを作成しておきます。
- resources/langフォルダー内の en フォルダーを複製して、複製したフォルダー名を ja に変更
- jaフォルダー内に sentinel.php というファイルを作成
- sentinel.php をエディターで開いて、以下のコードを追加
<?php return [ // ユーザー登録関連 'activate_title' => 'ユーザー登録を完了してください', 'after_register' => 'ご登録いただいたメールアドレスに、登録を完了するためのリンクを書いたメールを送信しました。メールを開いて、リンクを押して、ユーザー登録を完了させたら、以下からログインしてください。', 'register_user_title' => 'ユーザー登録', 'register_user_name' => '様', 'register_user_intro' => 'へのユーザー登録を完了するには、以下のボタン(あるいはリンク)をクリックしてください。', 'register_user_button' => 'ユーザーを有効にする', 'register_user_outro' => '----', 'notify_footer_message' => '*このメールは送信専用アドレスから送信しています。返信はできません。', 'notify_footer_caution' => 'ボタンを押してもうまく動作しない場合は、以下のURLをコピーして、Webブラウザーのアドレス欄にペーストしてみてください。', 'system_mail' => 'システムメール', ];
- 英文が必要な場合は、上記と同じキーに、英語を代入したものを en フォルダーの sentinel.php に追加する
- config/app.php をエディターで開く
- 'locale'と'fallback_locale'の2つの値を、'en' から 'ja' に変更する
以上で完了です。以降、sentinel.phpに追加した配列を、PHPのコードからは trans('sentinel.要素名')、bladeからは@lang('sentinel.要素名')で参照できます。
通知のスタブ作成
ユーザーをアクティベーションするためのメッセージを送信させる通知を作成します。この段階では、テストを通すことを優先して中身のないスタブクラスとします。
- ターミナルで、以下を実行して、ユーザー登録、登録完了、リマインダー、リマインダー完了の通知を作成
php artisan make:notification RegisterNotify
このままで、とりあえず指定のユーザーにメールを送信します。中身は後で実装します。
ルートの設定
コントローラーができたので、registerで呼び出したら登録するようにルートを設定します。テスト時に出てくるので、 login のルートも設定しておきます。
- routes/web.php をエディターで開く
- Auth::routes(); は、Laravelのauthは利用しないので削除
- POSTメソッドのregisterで登録を発動させるので、Auth::routes() があった場所に、以下を追加(ログイン用のルートを修正 2016/11/8)
// ログイン用のルート Route::get('login', function() {return view('auth.login', [ 'info' => session('info') ])->withErrors(session('myerror'));})->name('login'); Route::post('login', 'Auth\LoginController@login'); // ユーザー登録用のルート Route::get('register', function() {return view('auth.register');}); Route::post('register', 'Auth\RegisterController@register');
登録後のメッセージを表示できるように、loginビューに表示領域を追加します。
- resources/views/auth/login.blade.php をエディターで開く
- <div class="panel-body"> という行の下に、以下を追加(エラーの部分を修正 2016/11/8)
@if(isset($info)) <div class="alert alert-info"> {{ $info }} </div> @endif @include('parts.errors')
エラーのビューをJavaScriptやその他の文言に対応できるように修正します(2016/11/8追加)。
- resources/views/parts/errors.blade.php を作成して、エディターで開く
- 以下のコードを入力する
<!-- resources/views/parts/errors.blade.php --> <!-- Form Error List --> <div id="error-block" class="alert alert-danger" @if (count($errors) == 0) style="display: none;" @endif > <strong>以下のエラーが発生しました。</strong> <br> <ul id="error-list"> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div>
以上で、 view から info と myerror を受け取った場合、ログイン画面にメッセージを表示します。
Registerビューの調整
必要な項目は揃っているので変更しなくても構いません。表記を変更したり、表示を日本語にしたい、独自の欄を設けたい場合などの手順です。
- resources/views/auth/register.blade.php をエディターで開く
- 変更したい箇所を修正する
- 上書き保存
テスト
以上で最低限の機能が揃いました。 npm test をターミナルで実行してください。「OK (2 tests, 9 assertions)」と表示されて成功します。
また、 http://mailtrap.io に用意した Inbox の中身を確認してください。通知にあらかじめ用意されているメッセージが届きます。
アクティベーションコードを送るメール
通知時に送るメールのフォームを完成させます。すでに php artisan vendor:publish を実行しているので、必要なメールファイルが resources フォルダー内にありますので、それを編集します。
- resources/views/vendor/notifications/email.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') }} {{trans('sentinel.register_user_title')}} </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'] }}"> @if (! empty($greeting)) {{ $greeting }} @else @if ($level == 'error') Error! @else Hello! @endif @endif </h1> <!-- Intro --> @foreach ($introLines as $line) <p style="{{ $style['paragraph'] }}"> {{ $line }} </p> @endforeach <!-- Action Button --> @if (isset($actionText)) <table style="{{ $style['body_action'] }}" align="center" width="100%" cellpadding="0" cellspacing="0"> <tr> <td align="center"> <?php switch ($level) { case 'success': $actionColor = 'button--green'; break; case 'error': $actionColor = 'button--red'; break; default: $actionColor = 'button--blue'; } ?> <a href="{{ $actionUrl }}" style="{{ $fontFamily }} {{ $style['button'] }} {{ $style[$actionColor] }}" class="button" target="_blank"> {{ $actionText }} </a> </td> </tr> </table> @endif <!-- Outro --> @foreach ($outroLines as $line) <p style="{{ $style['paragraph'] }}"> {{ $line }} </p> @endforeach <!-- Salutation --> <p style="{{ $style['paragraph'] }}"> {{trans('sentinel.notify_footer_message') }} </p> <!-- Sub Copy --> @if (isset($actionText)) <table style="{{ $style['body_sub'] }}"> <tr> <td style="{{ $fontFamily }}"> <p style="{{ $style['paragraph-sub'] }}"> "{{ $actionText }}" {{trans('sentinel.notify_footer_caution')}} </p> <p style="{{ $style['paragraph-sub'] }}"> <a style="{{ $style['anchor'] }}" href="{{ $actionUrl }}" target="_blank"> {{ $actionUrl }} </a> </p> </td> </tr> </table> @endif </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'] }}"> © {{ 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>
- resources/views/vendor/notifications/email-plain.blade.php をエディターで開いて、以下に書き換える
<?php if (! empty($greeting)) { echo $greeting, "\n\n"; } else { echo $level == 'error' ? 'Error!' : 'Hello!', "\n\n"; } if (! empty($introLines)) { echo implode("\n", $introLines), "\n\n"; } if (isset($actionText)) { echo "{$actionText}", "\n", "{$actionUrl}", "\n\n"; } if (! empty($outroLines)) { echo implode("\n", $outroLines), "\n\n"; } echo trans('sentinel.notify_footer_message'), "\n"; echo config('app.name'), trans('sentinel.system_mail'), "\n";
以上で、通知用のメール書式が設定できました。引き続き、通知を作成します。
通知(Notify)を作成
通知は、連絡先の情報を持ったモデルから notify メソッドを呼び出すことで、メッセージを送ることができる機能です。メール以外にも、データベースやSMS、Slackなどに通知ができます。通知を利用するには、該当するモデルに Notifiable トレイトを宣言します。
ユーザー登録通知を作成する
先に作成した RegisterNotify.php の中身を実装して、ユーザーに確認用のリンクを送信します。
- app/Notifications/RegisterNotify.php を開いて、以下のようにする
<?php namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; class RegisterNotify extends Notification { use Queueable; /** 認証コード*/ private $code; /** * Create a new notification instance. * * @return void */ public function __construct($code) { $this->code = $code; } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { return ['mail']; } /** * Get the mail representation of the notification. * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable) { return (new MailMessage) ->subject(trans('sentinel.register_user_title')) ->greeting($notifiable->name.trans('sentinel.register_user_name')) ->line(config('app.name').trans('sentinel.register_user_intro')) ->action(trans('sentinel.register_user_button'), url('activate', [base64_encode($notifiable->email), $this->code])) ->line(trans('sentinel.register_user_outro')); } }
以上で、通知が完成です。 npm test でテストを実行して、成功することを確認してください。その後、 https://mailtrap.io を確認するとアクティベーションのためのリンクをつけたメールが届きます。HTMLとTextの中身をそれぞれ確認してみてください。
以上で、ユーザーの登録は出来上がりです。次は、アクティベーションを実装します。