tanaka's Programming Memo

プログラミングについてのメモ。

macのCodeceptionを使ってBrowserでテストをする(Firefoxでも動いた)

CodeceptionのAcceptanceテストではWebDriverを使って、ブラウザー上で実際に動作テストができます。問題は、Seleniumの新しいバージョンだと、Firefoxでテストするのが面倒になったことです。特にFirefoxでテストする必要がない場合は、Chromeで簡単にテストできます。(2017/2/25、 Firefoxでも動くようになっていました。)

  • WebDriver のインストールを Homebrew に変更しました(2017/2/27)
  • Firefox でも動くようになっていました(2017/2/25)
  • インストールに --dev フラグを追加 / 呼び出しを vendor/bin からに変更(2017/2/28)

環境

前提

手順

WebDriver のインストール

テストしたいブラウザー用の WebDriver をインストールします。以下、Google ChromeFirefox 用のインストールです。Safari は現時点でネイティブサポートされていて、WebDriver をインストールしなくても Selenium で利用できます。

すでにインストール済みの場合は、 Homebrew の更新をしてください。

brew install chromedriver
brew install geckodriver

以上できたら、環境セットアップへ進みます。インストール済みの場合は、以下で更新します。

Homebrewの更新
brew update
brew upgrade

環境セットアップ

  • プロジェクトフォルダーを作成する
  • Selenium公式ページから、Selenium Standalone Serverをダウンロードする
  • ダウンロードしたjarファイルをプロジェクトフォルダーに移動
  • 以下で、codeceptionをインストールして、環境ファイルを作成
composer require --dev codeception/codeception
php vendor/bin/codecept bootstrap
  • tests/acceptance.suite.yml をエディターで開く
  • 以下の内容にする
class_name: AcceptanceTester
modules:
    enabled:
        - WebDriver:
            url: http://www.google.co.jp
            browser: chrome
        - \Helper\Acceptance

(WebDriver のインストールを Homebrew に変更したので、以下は不要です。Windows の場合は以下をやってください。 2017/2/27)
テストしたいブラウザーの WebDriver を、 Selenium Standalone サーバーを起動するときのフォルダー(プロジェクトフォルダー直下が良いでしょう)に用意します。

  • Downloads - ChromeDriver - WebDriver for Chrome を開いて、最新版のChromeDriverを選択
  • 該当するドライバーをダウンロード。macであれば chromedriver_mac64.zip
  • zipファイルを解凍して、プロジェクトフォルダーにプログラムを置く

- FirefoxReleases · mozilla/geckodriver · GitHub
- それ以外のドライバーは、 http://www.seleniumhq.org/download/ の[Third Party Drivers, Bindings, and Plugins]以下から探してください

テストを作成

  • ターミナルから以下を実行してテストファイルを作成
php vendor/bin/codecept g:cept acceptance Welcome
  • tests/acceptance/WelcomeCept.php をエディターで開く
  • 以下のコードを書く
<?php
  $I = new AcceptanceTester($scenario);
  $I->wantTo('ensure that frontpage works');
  $I->amOnPage('/');
  $I->see('Google');

テストの実行

  • ターミナルを開いて、プロジェクトフォルダーに移動して、以下でサーバーを起動(テストに利用したい WebDriver が同フォルダーに必要です)
java -jar selenium-server-standalone-*.jar
  • 以下で、テスト実行
php vendor/bin/codecept run

以上で、Chromeが起動して、Googleのホームページが開かれて、Googleの文字があるのでテストは成功します。

まとめ

基本的には、Codeceptionの公式ページのWebDriverのヘルプの通りです。新しいFirefoxだと、署名のないAdd-onが使えなくなったことで、そのまま動かなくなっています。方法はあるようですが、面倒なので、browserの部分を「chrome」に変えればとりあえずChromeでのテストはできます。→2017/2/25 現在、 Firefox も動くようになりました。

もしかしたら、Chromeに開発用の設定が必要かも知れません。うちでは上記の通りで動作しました。

Laravel5.3でSentinelを利用する(7)アクティベーションコードの再送

前へ

ユーザー認証の仕上げに、アクティベーションコードの再送機能を追加します。アクティベーションコードには有効期限があるので、一定時間がすぎると利用できなくなります。また、ユーザーがメールを削除してしまう可能性もあるので、コードの再発行が必要となります。

(2016/12/21 テストにwantToを追加)


方針

アクティベーションコードを再送信する方針は以下の通りです。



テストを準備

先に作成していた ActivationUserCest に追加します。

  • tests/functional/ActivationUserCest.php をエディターで開く
  • tryToTest メソッドの最初に、以下のコードを追加(アクティベーション済みの場所ではテストにならないので)(2016/12/21 expectをwantToに変更)
        // アクティベーションの再送チェック
        $I->wantTo(' アクティベーションコードの再送.');
        $url = url('register', [base64_encode($this->cre['email'])]);
        $I->amOnPage($url);
        $I->seeInCurrentUrl('/login');
        $I->see(trans('sentinel.after_register'));

        // アクティベーションで無効なemail
        $I->wantTo(' 無効なメールアドレスでの要求に対してエラーを確認.');
        $url = url('register', [base64_encode('nobody@test.com')]);
        $I->amOnPage($url);
        $I->seeInCurrentUrl('/login');
        $I->see(trans('sentinel.invalid_activation_params'));
  • 登録済みのテストもしたいので、tryToTestメソッドの最後に以下のコードを追加
        // アクティベーションの再送チェック
        $I->wantTo(' アクティベーション済みに対する再送を確認.');
        $url = url('register', [base64_encode($this->cre['email'])]);
        $I->amOnPage($url);
        $I->seeInCurrentUrl('/login');
        $I->see(trans('sentinel.activation_done'));

上書きして、 npm test をターミイナルで実行してください。まだルートや処理がないので、エラーが発生します。実装を進めて、エラーをなくします。


ルートの作成

ルートを追加します。

  • routes/web.php をエディターで開く
  • Route::get('register', ・・・);の上に、以下のコードを追加する
Route::get('register/{email}', 'Auth\RegisterController@resendActivationCode');

コントローラーの作成

コントローラーを作成します。メールは、先に作成済みの RegisterNotify をそのまま利用します。

  • app/Http/Controller/Auth/RegisterController.php をエディターで開く
  • 以下の関数を、クラスに追加する
    /**
     * 指定のメールアドレスのアクティベーションコードを再送する
     */
    protected function resendActivationCode(Request $request) {
        // 古いアクティベーションコードを削除
        Activation::removeExpired();

        // ユーザーを確認
        $user = Sentinel::findByCredentials(['email' => base64_decode($request->email)]);
        if (is_null($user)) {
            return redirect('login')->with(['myerror' => trans('sentinel.invalid_activation_params')]);
        }

        // すでにアクティベート済みの時は、何もせずにログインへ
        if (Activation::completed($user)) {
            return redirect('login')->with(['info' => trans('sentinel.activation_done')]);
        }

        // アクティベーションの状況を確認
        $exists = Activation::exists($user);
        if (!$exists) {
            // 存在しない場合は、再生成して、そのコードを送信する
            $activation = Activation::create($user);
        }
        else {
            // 現在のコードを
            $activation = $exists;
        }

        // メールで送信する
        $usermodel = User::where('email', $user->email)->get()[0];
        $usermodel->notify(new RegisterNotify($activation->code));

        // メールを確認して、承認してからログインすることを表示するページへ
        return redirect('login')->with('info', trans('sentinel.after_register'));
    }

ビューの調整

アクティベーションが済んでいない時に、アクティベーションコードを再送信するためのリンクを表示するようにします。

  • resources/views/auth/login.blade.php をエディターで開く
  • パスワード欄と Remember Me のチェックボックスの間(53行目付近)に、以下のコードを追加
                        @if(isset($resend_code))
                        <div class="form-group">
                            <div class="col-md-6 col-md-offset-4">
                                <a class="btn btn-link" href="{{ url('register', base64_encode($resend_code))}}">
                                    ユーザー登録を完了させるメールを再送
                                </a>
                            </div>
                        </div>
                        @endif

以上で完了です。 npm test でテストを実行すると成功するようになります。

手動で動きを確認する場合は、 http://localhost:8000 に接続して、ユーザー登録をした後、アクティベーションをメールで行う前に、ログインを試してみてください。アクティベートが未完了である旨、メッセージが表示されて、アクティベートコードの再送信のリンクが表示されます。リンクをクリックすると、アクティベートコードが再送信されます。その時点で、アクティベートコードが古くなっていたら、新しいアクティベートコードを生成して、そのコードをメールします。


      • -

以上で、Laravelの認証の機能を全て置き換えることができました。ユーザー登録にメールによる確認が必要になるのはメリットが大きいです。

編集可能なロールやパーミッションを使った認可機能がつけば、さらにSentinelに変更した効果が高まります。

Laravel5.3対応のものは、後日作成します。



前へ


Laravel5.3でSentinelを利用する(6)パスワードのリセット

前へ | 次へ

パスワードを忘れた時の対応を実装します。

(2016/12/21 HTMLのidをケバブケースに修正)
(2016/12/21 テストにwantToを追加)


はじめに

login画面のパスワードリセットに対応します。SentinelとLaravelでは手続きの内容が異なるので、自前で実装します。流れは以下の通りです。

  1. GET password/reset
    1. リセット用の画面を表示
    2. メールアドレスを入力させて、POSTで送信
  2. POST password/email
    1. メールアドレスを受け取る
    2. SentinelのReminderを作成して、リセットトークンを生成
    3. コードを添えたメールを送信
  3. GET password/reset/{token}
    1. メールから上記にアクセスさせて、リセットトークンを送信内容に含める
    2. メールアドレスとパスワード、パスワードの確認の3つを入力してPOST送信
  4. POST password/reset
    1. 上記のデータを受け取って、パスワードのリセットを実施する



テストの作成

パスワードの再発行のためのテストを作成します。

ボタンを押すフォームを指定するためのIDをビューに追加します。

  • resources/views/auth/passwords/email.blade.php を開く
  • 「<form」から始まる行を探して、以下のように id 属性を追加する(2016/12/21 HTMLのidをケバブケースに修正)
                    <form id="send-reset-form" class="form-horizontal" role="form" method="POST" action="{{ url('/password/email') }}">
  • resources/views/auth/passwords/reset.blade.php を開く
  • <form から始まる行を探して、以下のように id 属性を追加する(2016/12/21 HTMLのidをケバブケースに修正)
                    <form id="reset-password-form" class="form-horizontal" role="form" method="POST" action="{{ url('/password/reset') }}">

必要であれば、ページの内容も書き換えてください。

続いて、テストコードを作成します。

  • ターミナルで以下を実行して、テスト用のCestファイルを作成
composer exec codecept g:cest functional ResetPassword
  • tests/functional/ResetPasswordCest.php をエディターで開く
  • 以下のようにする(2016/12/21 テストにwantToの追加と、submitFormのIDをケバブケースに修正)
<?php

use Sentinel;
use Reminder;

class ResetPasswordCest
{
    private $cre = [
        'name' => 'パスワード再発行',
        'email' => 'reset@test.com',
        'password' => 'password'
    ];

    public function _before(FunctionalTester $I)
    {
        // ユーザーを作成
        Sentinel::registerAndActivate($this->cre);
    }

    public function _after(FunctionalTester $I)
    {
    }

    // tests
    public function tryToTest(FunctionalTester $I)
    {
        // 存在しないメールへのリクエスト
        $I->wantTo(' 存在しないメールへのリクエストの動作確認.');
        $I->amOnPage('/password/reset');
        $I->submitForm('#send-reset-form', [
            'email' => 'nobody@test.com'
        ]);
        $I->seeInCurrentUrl('/login');

        // メール発行(resetとemailのテスト)
        $I->wantTo(' メール発行(resetとemailのテスト)');
        $I->amOnPage('/password/reset');
        $I->submitForm('#send-reset-form', [
            'email' => $this->cre['email']
        ]);
        $I->seeInCurrentUrl('login');
        $I->see(trans('sentinel.password_reset_sent'));

        // メール発行テスト
        $I->expect(' 登録したemailアドレスが見つかる.');
        $user = Sentinel::findByCredentials($this->cre);
        \PHPUnit_Framework_Assert::assertEquals($user->email, $this->cre['email']);
        $I->expect(' 登録したユーザーのリマインダーが登録されている.');
        $reminder = Reminder::exists($user);
        \PHPUnit_Framework_Assert::assertNotFalse($reminder);

        // 不正なトークンでのアクセスして、パスワードの変更を試みる
        $I->wantTo(' 不正なトークンでアクセスして、パスワードの変更を試みた時に失敗.');
        $I->amOnPage('/password/reset/01234567891123456789212345678931');
        $I->submitForm('#reset-password-form', [
            'email' => $this->cre['email'],
            'password' => $this->cre['password'],
            'password_confirmation' => $this->cre['password'],
        ]);
        $I->see(trans('sentinel.password_reset_failed'));

        // 正しいトークンでのアクセスして、パスワード確認のミス
        $I->wantTo(' 正しいトークンでアクセスして、パスワードの確認が間違いの時の動作.');
        $I->amOnPage('/password/reset/'.$reminder->code);
        $I->submitForm('#reset-password-form', [
            'email' => $this->cre['email'],
            'password' => $this->cre['password'],
            'password_confirmation' => 'invalidinvalid',
        ]);
        $I->see('does not match');

        // 正しいトークンでのアクセスして、パスワード変更
        $I->wantTo(' パスワード変更の実行確認.');
        $I->amOnPage('/password/reset/'.$reminder->code);
        $I->submitForm('#reset-password-form', [
            'email' => $this->cre['email'],
            'password' => 'resetpassword',
            'password_confirmation' => 'resetpassword',
        ]);
        $I->see(trans('sentinel.password_reset_done'));

        // パスワードが変更されたことを確認
        $I->expect(' 変更後のデータが見つかる.');
        $check = [
            'email' => 'reset@test.com',
            'password' => 'resetpassword'
        ];
        \PHPUnit_Framework_Assert::assertNotFalse(Sentinel::authenticate($check));
        // パスワードが変更されたことを確認
        $I->expect(' パスワードが変更されていることを確認.');
        $check = [
            'email' => 'reset@test.com',
            'password' => $this->cre['password']
        ];
        \PHPUnit_Framework_Assert::assertFalse(Sentinel::authenticate($check));
    }
}



ルートの作成

パスワードリセット画面の呼び出しと、パスワードリセットを実行するPOSTルートを定義します。

// パスワードのリセット用のルート
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');
Route::post('password/reset', 'Auth\ResetPasswordController@reset');



リマインダーに必要なメッセージを追加

リマインダーに関連する文言をメッセージに追加しましょう。

  • resources/lang/ja/sentinel.php をエディターで開く
  • 以下のメッセージを配列に追加する
    // パスワードの再設定関連
    'password_reset_sent' => 'パスワードを再設定するためのメールを送信しました。届いたメールに記載のリンクをクリックして、パスワードの再設定画面を開いてください。',
    'reminder_title' => 'パスワードをリセットできます。',
    'password_reset_done' => 'パスワードをリセットしました。',
    'password_reset_failed' => 'リセットコードが古くなっていました。もう一度、リセットし直してください。',



メールのビューを変更する場合

パスワードをリセットするためのメールの本文は、Laravelのものをそのまま利用します。変更したい場合は、以下のファイルを書き換えてください。

  • resources/views/auth/passwords/email.blade.php



パスワードのリセットメールを送付するメソッドの作成

既存の ForgotPasswordController.php の該当するメソッドを上書きします。

  • app/Http/Controllers/Auth/ForgotPasswordController.php をエディターで開く
  • SentinelとReminder、既存のメール通知、Requestを使えるように、冒頭に以下の4つのuseを追加する。既存のuse文も必要なので、そのまま残しておく
use Reminder;
use Sentinel;
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
use Illuminate\Http\Request;

リマインダーメールを送信する処理を追加します。

  • クラス内に以下のコードを追加。既存のコードは残しておくこと
    /**
     * パスワードを再設定するための処理
     * ユーザーが有効で、パスワードが条件に合致していたら、SentinelのReminderを使って処理する
     */
    protected function sendResetLinkEmail(Request $request) {
        // 古いリマインダーコードを削除
        Reminder::removeExpired();

        // チェック
        $this->validate($request, [
            // emailは必須で、emailの形式で、255文字まで
            // メールアドレスの有無は、不正を避けるためにチェックしない
            'email' => 'required|email|max:255'
        ]);

        // ユーザーを検索
        $user = Sentinel::findByCredentials(['email'=>$request->email]);
        if (is_null($user)) {
            // ユーザーがいなければ成功したような感じにしてログイン画面へ
            return redirect('login')->with(['info'=>trans('sentinel.password_reset_sent')]);
        }

        // リマインダーが作成済みなら、それを再送信する
        $code = "";
        $exists = Reminder::exists($user);
        if ($exists) {
            // すでに設定されているので、リマインダーコードを設定
            $code = $exists->code;
        }
        else {
            // 新規にリマインダーを作成して、コードを返す
            $reminder = Reminder::create($user);
            $code = $reminder->code;
        }

        // メールを送信
        $user->notify(new ResetPasswordNotification($code));

        // 成功したら、login画面へ移動
        return redirect('login')->with(['info'=>trans('sentinel.password_reset_sent')]);
    }

以上で、既存の ResetPasswordNotification を使って、リセット用のコードをメールで送付します。 npm test を実行すると、エラーが出ますが、メールは送信されます。 http://mailtrap.io にメールをリセットするためのメールが届いていることを確認してください。


パスワードのリセットの実行

パスワードのリセット画面の表示は、既存のままでOKです。最後に、実際にリセットを実行するコントロールメソッドを作成します。

  • app/Http/Controllers/Auth/ResetPasswordController.php をエディターで開く
  • ファイルの最初の方に、利用したいFacadeのための以下のuseを追加
use Sentinel;
use Reminder;
use Illuminate\Http\Request;
  • 以下の reset メソッドを、クラス内に追加する
    /**
     * パスワードを再設定するための処理
     * ユーザーが有効で、パスワードが条件に合致していたら、SentinelのReminderを使って処理する
     */
    protected function reset(Request $request) {
        // 古いリマインダーコードを削除
        Reminder::removeExpired();

        // チェック
        $this->validate($request, [
           // emailは必須で、emailの形式で、255文字まで
           // メールアドレスの有無は、不正を避けるためにチェックしない
           'email' => 'required|email|max:255',
           // passwordは必須で、6文字以上255文字以下で、確認欄と一致する必要がある
           'password' => 'required|between:6,255|confirmed',
           // トークンは必須で、32文字
           'token' => 'required|size:32'
       ]);

       // ユーザーを検索
       $user = Sentinel::findByCredentials(['email'=>$request->email]);
       if (is_null($user)) {
           // ユーザーがいなければ成功したような感じにしてログイン画面へ
           return redirect('login')->with(['info'=>trans('sentinel.password_reset_sent')]);
       }

       // リマインダー実行
       $reminder = Reminder::complete($user, $request->token, $request->password);
       if ($reminder) {
           // 成功
           return redirect('login')->with(['info' => trans('sentinel.password_reset_done')]);
       }

       // 失敗
       return redirect('login')->with(['myerror'=>trans('sentinel.password_reset_failed')]);
   }

以上で完了です。 npm test でテストが成功します。 http://localhost:8000/ を開いて、手動でパスワードのリセットができることも確認してみてください。



前へ | 次へ

Laravel5.3でSentinelを利用する(5)ログインとログアウト(2016/12/31更新)

前へ | 次へ

ログイン処理を、Sentinelに変更します。ルートは前の手順ですでに作成済みなので、postに対するコントローラーの作成を行います。

(2016/11/8 エラーを withErrors で渡すように修正)
(2016/12/15 HTMLのID 'loginForm'がキャメルケースだったので、ケバブケースに修正)
(2016/12/21 テストにwantToを追加)
(2016/12/31 ログアウト後表示するページを home から login に変更)


ログイン

ログインのテストコードの作成

ログインに関する処理のテストコードを用意します。

まずは、ボタンを押すフォームを指定するためのIDをformタグに追加します。

  • resources/views/auth/login.blade.php をエディターで開く
  • 「<form」から始まる行を探して、以下のようにID属性を追加する
                    <form id="login-form" class="form-horizontal" role="form" method="POST" action="{{ url('/login') }}">

続けて、Cestファイルを作成します。

  • ターミナルから以下を実行して、ログイン用のCestファイルを作成
composer exec codecept g:cest functional Login
  • tests/functional/LoginCest.php をエディターで開いて、以下のコードにする(2016/12/21 テストにwantToを追加)
<?php

use Activation;
use Sentinel;

class LoginCest
{
    // ユーザー情報
    private $cre = [
        'name' => 'ログイン',
        'email' => 'login@test.com',
        'password' => 'password'
    ];

    public function _before(FunctionalTester $I)
    {
        // ユーザーを登録
        Sentinel::registerAndActivate($this->cre);

    }

    public function _after(FunctionalTester $I)
    {
    }

    // tests
    public function tryToTest(FunctionalTester $I)
    {
        // 無効なログインテスト
        $I->wantTo(' 未登録ユーザーのログイン失敗');
        $I->amOnPage('/login');
        $I->submitForm('#login-form', [
            'email' => 'nobody@test.com',
            'password' => 'notentry'
        ]);
        $I->see(trans('sentinel.login_failed'));

        // ログインの成功テスト
        $I->wantTo(' ログイン動作の確認.');
        $I->amOnPage('/login');
        $I->submitForm('#login-form', $this->cre);
        $I->seeInCurrentUrl('/home');
    }
}

npm test でテストを走らせると、まずはエラーになります。機能を実装してテストが通るようにします。

必要なメッセージの追加

ログインに関連するメッセージを追加します。

  • resources/lang/ja/sentinel.php をエディターで開く
  • 以下のメッセージを追加
    // ログイン関連
    'not_activation' => 'ユーザー登録が完了していません。登録したメールアドレスに、登録確認用のメールを送信してあります。メールを開いて、リンクをクリックしてください。メールを紛失した場合は、下のリンクからメールを再送できます。',
    'login_failed' => 'ログインに失敗しました。正しいメールアドレスとパスワードでログインしてください。',
    'login_throttling' => 'ログイン失敗が規定回数を越えました。一定時間、IPを凍結します。',

ログイン処理の実装

既存のログイン処理のコードを、Sentinelに合わせて書き換えます。

アクティベーションされていない時や、ログイン失敗時の処理もまとめて作成します。アクティベーションされていない時には、アクティベーションコードの再送信をする指示をビューに投げていますが、この辺りの実装は後ほど行います。

  • app/Http/Controllers/Auth/LoginController.php をエディターで開く
  • ファイルの中身を、以下のように書き換える(エラーをwithErrorsで渡すように修正)
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Cartalyst\Sentinel\Checkpoints\NotActivatedException;
use Cartalyst\Sentinel\Checkpoints\ThrottlingException;
use Sentinel;

class LoginController extends Controller
{
    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest', ['except' => 'logout']);
    }

    /**
     * ログイン処理
     */
    public function login(Request $request) {
        // バリデーション
        $this->validate($request, [
            'email' => 'required|email|max:255',
            'password' => 'required|between:6,255',
            'remember' => 'boolean',
        ]);

        // 認証処理
        try {
            $this->userInterface = Sentinel::authenticate([
                'email' => $request['email'],
                'password' => $request['password']
            ], $request['remember']);
        } catch (NotActivatedException $notactivated) {
            return view('auth.login', [
                'resend_code' => $request['email']
            ])->withErrors([trans('sentinel.not_activation')]);
        } catch (ThrottlingException $throttling) {
            return view('auth.login')->withErrors([trans('sentinel.login_throttling')."[あと".$throttling->getDelay()."秒]"]);
        }

        if (!$this->userInterface) {
            // エラー
            return view('auth.login')->withErrors([trans('sentinel.login_failed')]);
        }

        return redirect($this->redirectTo);
    }
}

以上で、ログイン、未アクティベーション、IP凍結に対応しました。 npm test で実行したテストが成功します。また、 http://localhost:8000 を開いて、先に登録したユーザーでログインを試すと、今度は home ページに移動して、ログインが成功します。


ログアウト

ログアウトに対応させます。

テストの作成

テストはシンプルなので、Loginのものに実装します。

  • tests/functional/LoginCest.php をエディターで開く
  • tryToTest メソッドのログイン用のテストコードの下に、以下のコードを追加する(2016/12/21 テストにwantToを追加)
        // POSTでログアウトテスト
        $I->wantTo(' ログアウトの動作確認.');
        $I->amOnPage('/login');
        $I->submitForm('#login-form', $this->cre);
        $I->submitForm('#logout-form', []);
        $I->seeInCurrentUrl('/login');

        // GETでログアウトテスト
        $I->wantTo(' GETでのログアウトの動作確認.');
        $I->amOnPage('/logout');
        $I->seeInCurrentUrl('/login');

ルートの追加

ログアウトのルートを設定します。フォームからのリクエストは、postで送信されるので、getとpostの双方にルートを設定します。

  • routes/web.php をエディターで開く
  • 以下のルートをログイン関連の下あたりに追加する
// ログアウト用のルート
Route::match(['get', 'post'], 'logout', 'Auth\LoginController@logout')->name('logout');

コントローラーを追加する

コントローラーはシンプルなので、Loginのものに追加します。

  • app/Http/Auth/LoginController.php
  • 以下の変数をクラス内に追加する
    /**
     * ログアウト後に表示するページ
     */
    protected $logoutTo = '/login';
  • 以下のlogoutメソッドをクラスに追加する(2016/12/31 ログアウト後のリダイレクト先を logoutTo に変更)
    /**
     * ログアウト処理
     */
    protected function logout(Request $request) {
        Sentinel::logout();

        return redirect($this->logoutTo);
    }

ログアウトの実装完了です。 npm test を実行して、成功することを確認して下さい。

以上で、ログインとログアウトが実装できました。 http://localhost:8000/ を開いて、ログインとログアウトを試してください。テストが通っているので、問題なく成功します。



これで最低限の流れは実装できました。残りは、アクティベーションコードの再送や、パスワードの対応などです。



前へ | 次へ

Laravel5.3でSentinelを利用する(4)アクティベーション

前へ | 次へ

ユーザー登録ができたので、アクティベーションコードを確認して、ユーザーを有効にする処理を実装します。

(2016/12/21 テストにwantToを追加)


テストの作成

まずは、アクティベーションをするテストコードを作成しましょう。

  • ターミナルから以下を実行して、 ActivationUserCest.php を作成
composer exec codecept g:cest functional ActivationUser
  • tests/functional/ActivationUserCest.php をエディターで開いて、以下のコードに書き換える(2016/12/21 expectをwantToに書き換え)
<?php

class ActivationUserCest
{
    private $code = "";
    // ユーザーを登録
    private $cre = [
        'name' => 'アクティベーション',
        'email' => 'act@test.com',
        'password' => 'password'
    ];

    public function _before(FunctionalTester $I)
    {
        $user = Sentinel::register($this->cre);

        // アクティベーション作成
        $act = Activation::create($user);
        $this->code = $act->code;
    }

    public function _after(FunctionalTester $I)
    {
    }

    // tests
    public function tryToTest(FunctionalTester $I)
    {
        // コード違い
        $I->wantTo(' アクティベーションコード違いエラーの確認.');
        $url = url('activate', [base64_encode($this->cre['email']), 'error']);
        $I->amOnPage($url);
        $I->see(trans('sentinel.invalid_activation_params'), '.alert-danger');

        // 成功チェック
        $I->wantTo(' アクティベーション成功の確認.');
        $url = url('activate', [base64_encode($this->cre['email']), $this->code]);
        // リンクを amOnPage で表示
        $I->amOnPage($url);
        // 登録完了のメッセージをチェック
        $I->see(trans('sentinel.activation_done'));

        // すでにアクティベーション済みの場合は、普通にログイン画面へ
        $I->wantTo(' アクティベーション済みはログイン画面へ.');
        $url = url('activate', [base64_encode($this->cre['email']), 'no']);
        $I->amOnPage($url);
        $I->dontSee(trans('sentinel.activation_done'));
        $I->dontSee(trans('sentinel.invalid_activation_params'));

        // 失敗チェック
        $I->wantTo(' アクティベーションメールもアクティベーションコードも違う時の確認.');
        $url = url('activate', [base64_encode('nobody@test.com'), 'no']);
        $I->amOnPage($url);
        $I->see(trans('sentinel.invalid_activation_params'), '.alert-danger');
    }
}

npm test でテストを走らせるとエラーになります。これから実装を進めてテストが完了するようにします。


アクティベーション用のルートを作成

メールからは、 activate/{メールアドレス}/{アクティベーションコード} という形式でアクティベーション用のアクセスがあります。これを受け取って、処理するためのルートを追加します。

  • routes/web.php をエディターで開く
  • registerの登録の下に、以下を追加
// アクティベーション
Route::get('activate/{email}/{code}', 'Sentinel\ActivateController@activate');

アクティベーションの実行

ルートを定義したので、それを処理するコントローラーを作成します。

ルート内のパラメーター({email}と{code})は、Requestに渡されるので、$request->emailや$request->codeのようにアクセスすることができます。

  • ターミナルで以下を実行
php artisan make:controller Sentinel/ActivateController
  • app/Http/Controller/Sentinel/ActivateController.php をエディターで開く
  • ファイルの最初の方に、以下の use を追加
use Activation;
use Sentinel;
  • 以下の activate() メソッドをクラスに追加する
    /**
     * アクティベーション
     */
    protected function activate(Request $request) {
        // ユーザーを取得する
        $user = Sentinel::findByCredentials(['email' => base64_decode($request->email)]);
        if (is_null($user)) {
            return redirect('login')->with(['myerror' => trans('sentinel.invalid_activation_params')]);
        }

        // アクティベーション済みだった場合、そのまま戻る
        if (Activation::completed($user)) {
            return redirect('login');
        }

        // アクティベーションを実行する
        if (!Activation::complete($user, $request->code)) {
            return redirect('login')->with(['myerror' => trans('sentinel.invalid_activation_params')]);
        }

        return redirect('login')->with(['info' => trans('sentinel.activation_done')]);
    }

必要なメッセージの追加

アクティベーション関連のメッセージを追加します。

  • resources/lang/ja/sentinel.php をエディターで開いて、以下を追加
    // アクティベーション関連
    'invalid_activation_params' => 'アクティベーションの情報が一致しませんでした。',
    'activation_done' => 'ユーザー登録を完了しました。ログインして、サービスをご利用ください。',

以上で、アクティベーション処理は完成です。 npm test でテストを走らせると、「OK (3 tests, 14 assertions)」が表示されて成功します。

実際に動きを確認する場合は、 http://localhost:8000 を開いて、登録の流れを試してください。また、 http://localhost:8000/activate/bademail/badcode にアクセスすると、アクティベーションのパラメーターが不正である旨、表示されます。

ユーザーは有効にできましたが、まだログインが Sentinel に対応していないので、アクティベーションしたユーザーでのログインはできません。次はこれを対応させましょう。



前へ | 次へ

Laravel5.3でSentinelを利用する(3)ユーザー登録(2016/11/8更新)

前へ | 次へ

環境の構築が完了したので、実際に機能を置き換えていきます。まずはユーザー登録を対応させます。

(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 を利用することにします。

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'] }}">
                                            &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>
  • 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の中身をそれぞれ確認してみてください。



以上で、ユーザーの登録は出来上がりです。次は、アクティベーションを実装します。



前へ | 次へ

Laravel5.3でSentinelを利用する(2)MiddlewareをSentinelに対応させる

前へ | 次へ

LaravelとSentinel、Codeceptionのインストールが完了したら、次にMiddlewareをSentinelに対応させます。


はじめに

ログインなどの処理の流れは Laravel に組み込まれています。そこにSentinel用のGuardを組み込んで対応させます。

Laravelの認証は、 Guard と Provider によって実行されます。 Guard とは、ユーザーが認証済みかを判定する方法を定義するもので、 Provider は、ユーザーの情報をEloquentやデータベースなどから取り出す方法を定義するものです。

Sentinel用のこれらを作成して公開してくれていましたので、これを利用します。
GitHub - joshwhatk/sentinel-guard: Laravel Guard for Cartalyst's Sentinel

ライセンスは MIT License です。

必要なプログラムを組み込む

  • GitHub - joshwhatk/sentinel-guard: Laravel Guard for Cartalyst's Sentinel をWebブラウザーで開く
  • ウィンドウの右にある[Clone or download]をクリックして、[Download ZIP]を選択
  • ダウンロードが完了したら、ファイルを解凍する
  • Laravelのプロジェクトフォルダー内に、 app/Joshwhatk/Cent フォルダーを作成
  • 解凍したフォルダー内の src の中にある [Cent.php][CentServiceProvider.php][UserModel.php]の3つのファイルをコピーして、作成した app/Joshwhatk/Cent フォルダーに貼り付ける
  • composer.json をエディターで開く
  • "autoload"の"psr-4"の定義を以下のように修正
        "psr-4": {
            "App\\": "app/",
            "Joshwhatk\\Cent\\": "app/Joshwhatk/Cent/"
        }

以上で必要なファイルの組み込みができました。解凍したフォルダーなどは削除しても構いません。

Cent.php を5.3に対応させる

現状のままだと、いくつか機能が足りません。 GuardHelpersトレイトを Cent.php に組み込んで対応します。

  • app/Joshwhatk/Cent/Cent.php をエディターで開く
  • ファイルの上の方に、以下の use 文を追加
use Illuminate\Auth\GuardHelpers;
  • クラスの宣言内に、以下のトレイトの呼び出しを追加
    use GuardHelpers;

ユーザーモデルを作成

Sentinel のデータベースに対応したユーザーモデルを用意して、認証に組み込むための機能も追加します。

  • app/User.php をエディターで開く
  • 以下のようにコードを修正
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Joshwhatk\Cent\UserModel;

class User extends UserModel
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'email',
        'password',
        'name',
        'permissions',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
    ];
}

サービスプロバイダーを登録

  • config/app.php をエディターで開く
  • $providers 配列に、以下を追加
        Joshwhatk\Cent\CentServiceProvider::class,

作成した cent という名前の Guard を設定する

  • config/auth.php を開く
  • 'guards' の定義の 'driver' を以下のように全て cent に変更する
    'guards' => [
        'web' => [
            'driver' => 'cent',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'cent',
            'provider' => 'users',
        ],
    ],

設定を反映させる

以上、設定した内容を環境に反映させます。

  • ターミナルで以下を実行して、 composer を再構成する
composer update
  • php artisan serve でサービスを起動しているターミナルで、[control]+[C]を押して、サービスを停止
  • 改めて以下をターミナルで実行して、サービスを再開させる
php artisan serve

以上で、認証のための auth ミドルウェア が Sentinel に対応しました。 http://localhost:8000 を再読み込みして、正常に表示されることを確認してください。また、 npm test も成功します。

次は、ユーザー登録を実装します。



前へ | 次へ