Laravelでログインしたときにいろいろやる

アクションフックみたいな話があるんですが、Laravel5.4も似たようなものがなくはないといった感じです。リスナーとかイベントとかがそれにあたるでしょうか。あるいはイベントとかリスナーを使ってアクションフック的なものを作るといったことになるでしょうか。
まずは、ログインの機能の全体を見てみましょう。その中で自分の望む場所に機能を入れ込むのがよいと思います。

ログインの流れ

ログイン時の最初のインターフェイスはこのコントローラになります。

app/Http/Controllers/Auth/LoginController.php

このファイル自体は非常に簡素です。ログインしたときのトップページになるパスにリダイレクト指定。それ以外の場合はゲストとしての例外処理としてログアウト、というだけになります。
これらの処理の具体的なコードは、

Illuminate\Foundation\Auth\AuthenticatesUsers;

にあるようです。vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.phpというtraitが実態になります。見た感じだと「credentials()」あたりでEmailとパスワードを受け取ってログインの処理をしているようです。そのメソッドは、attemptLogin()の中で使われています。たぶんここにEmailとパスワードを渡してあげればいいという感じですね。attemptLogin()はlogin()の最後に実行されてます。(*1)
attemptLogin()が評価はされると(たぶんtrueを返すと)sendLoginResponse()が実行されるという段取りです。
sendLoginResponse()は以下のようになってます。非常に短いです。

///vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php

protected function sendLoginResponse(Request $request)
{
    $request->session()->regenerate();

    $this->clearLoginAttempts($request);

    return $this->authenticated($request, $this->guard()->user())
            ?: redirect()->intended($this->redirectPath());
}

*1 実処理の他にセキュリティー対策みたいな感じで何度もアクセスしてきたやつをどうするか?とかバリデーションとかログインに必要な幾つかのことをやってます。

ログインしたときに何かする

単純にログインしたときという場合には、LoginController.phpのコンストラクタでやるのがいちばんよいと思います。100%ここを通ってログインするわけですから。しかしここでは最初のログイン時のみ利用される場所であることと、抽象度がそれほど高くないところなのです。実際のログイン処理の前に通る通過点なので、実際の認証が行われた後に何かしたい場合には適しておりません。

//app/Http/Controllers/Auth/LoginController.php
public function __construct()
{
    // ここら辺りに何か書く
    $this->middleware('guest')->except('logout');
}

じゃどこがいちばんいいかというとLaravel5.4ではそのメソッドを空で用意してくれていたので気が効いているなぁといったところです。
vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.phpには、

/**
 * The user has been authenticated.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  mixed  $user
 * @return mixed
 */
protected function authenticated(Request $request, $user)
{
    // ここら辺りに何か書く
}

という感じになっていて、ここは認証後にいろいろできる場所ということになります。とはいえ、ここはvendorの中のトレイト中なので、実際はこのメソッドをフロントコントローラのLoginController.phpでオーバーライドするということになります。フロントコントローラで使う際には、

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

こいつを呼んでおかないとエラーになりますよ。Laravelに限らず最近のフレームワークはクラス(とかトレイト)のオーバーライドや継承というのが基本的な使い方になると思います。
で、例えばログインの度にログをとることを考えてみます。

ログインのログをとる

ここではユーザー名とIPアドレスを取得してみましょう。先のauthenticated()ではすでに$userがパラメータとして設定してあるので、ここにログイン後の情報が山ほど入っています。試しにダンプしてみるとデフォルトのユーザー情報がたぶん全部入っていると思われます。(ちゃんと確かめてないけど、たぶん)ですので、ユーザー名はすでに取得できてます。
サンプルのコードを書くと以下のような感じになります。こっちの方も参照してみてください。

ログと関係のない場所を端折っておりますが、LoginController.phpでは以下のようになります。

//app/Http/Controllers/Auth/LoginController.php

// Monolog
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

class LoginController extends Controller
{
    /**
     * Monologのカスタムログ(ログイン認証のロギング)
     * 2017-08-26
     * @var string
     */
    protected $loginlog;

    public function __construct()
    {
        $this->loginlog = new Logger('loginlog');
        $this->loginlog->pushHandler(new StreamHandler("SOME_PATH/storage/logs/login.log", Logger::INFO));
        ...
    }

     /**
     * 認証後にいろいろやるところ
     * 
     * @from vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
     * @param  \Illuminate\Http\Request  $request
     * @param  mixed  $user
     * @return mixed
     */
    protected function authenticated(Request $request, $user)
    {

        Log::info('ログイン ID:'.$user);
        $this->loginlog->addInfo('Login ID:'.$user);
    }
}

Monologはvenderにあるものを使います。useすればOKだと思います。
それからコンストラクタで初期設定をしてあげる。この際にカスタマイズしたログを指定することができます。後は作成したオブジェkとからaddInfo()メソッドで適当に書いておけば完了です。


Laravel 5.4 エラーとログ Monolog

Last update: 2017.08.27 (日)