選べる全4種類!Laravelで最終ログイン&ログアウト日時を保存(たった4行〜イベントまで)

さてさて、これはLaravelに限らずどんな開発でもそうなのですが、ある機能を実装する場合、条件によっては全く違ったアプローチをすることがあります。

例えば、チーム制の場合だとより他の人が分かりやすい構造を意識して開発を進めていくべきですし、単に個人的なサイトなら一番シンプルな方法でも問題はありません。

そして、今回の話題として選んだ「最終ログインの保存」に関してもこの例にもれず、様々なパターンのアプローチがあるので、せっかくなので思いついた全ての方法をまとめて公開することにしました。

ぜひ学習者さんたちも含めて参考になれば幸いです!

※ 開発環境は、Laravel 5.7+PHP 7です。

ユーザーテーブルの準備

※ 前提としてLaravelログイン機能を有効にしておいてください。やり方は、【Laravel5.6】インストール直後にやること3点の「ログイン、パスワードリマインダー機能を構築」で説明しています。

では、まず最後にログインした日時を保存するためのlast_login_atフィールドをUserテーブルに追加しましょう。

後から追加する形式でもいいのですが、よりシンプルにいきたいので、すでにある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->dateTime('last_login_at')->nullable(); // ←追加。nullもOK。
        $table->rememberToken();
        $table->timestamps();
    });
}

では、マイグレーションを始めからやり直すために以下のコマンドを実行します。

php artisan migrate:fresh

※ もしSeederを用意している場合は、一緒に実行する場合は--seedもつけて実行するといいでしょう。

php artisan migrate:fresh --seed

コマンドを実行するとDBテーブルはこうなります。

では、次から実際に最終ログイン日時を保存していきます。

最終ログイン日時を保存する方法

たった4行で実装する一番シンプルな方法

Laravelのログイン機能を有効にすると、app/Http/Controllers/Authというフォルダが作成され、この中にログイン全般に関連する以下5つのコントローラーが作成されることになります。

  • ForgetPasswordController
  • LoginController
  • RegisterController
  • ResetPasswordController
  • VerificationController

そして、ログイン時の管理をするコントローラーLoginController.phpの中にauthenticated()というメソッドを追加してコードを書くだけで最終ログイン時間の保存をすることができるようになります。

protected function authenticated(Request $request, $user)
{
    $user->last_login_at = now();
    $user->save();
}

authenticated()AuthenticatesUsersというトレイトの中で定義されているメソッドなので、初期状態ではLoginController.phpには記述されていません。

なお、引数のRequestはネームスペースが必要なので注意が必要です。

use Illuminate\Http\Request;

この方法はとてもシンプルなので、保守管理にそれほどこだわる必要がない小規模なサイトに向いているといえます。

ログイン・イベントで実装する

先ほどの方法はたった4行で書けるのでとてもシンプルですが、authenticated()の存在を知らない人間からすると直感的にここを見つけるのは難しく、チームで開発している場合はベターな方法とは言えないかもしれません。

また、最終ログイン情報だけでなくIPアドレス等さまざまな情報を保存するなど、付随するコードが多くなってしまった場合は、専用ファイルを作ってカプセル化し、より保守管理をしやすくしておくべきです。

そこで、2つ目はログイン・イベントを使って実装する方法を紹介します。

まずはapp/Providers/EventServiceProvider.phpを開いて、以下のようにログイン成功イベントを登録します。

protected $listen = [
    // ログイン成功したら実行
    'Illuminate\Auth\Events\Authenticated' => [
        'App\Listeners\LogAuthenticated',
    ],

    // 省略

ただし、この状態ではまだ起動するイベント・リスナーが存在していないので、以下のコマンドで自動作成します。

php artisan event:generate

すると、app/Listeners/LogAuthenticated.phpが作成されるので、このファイルを開いてhandle()内に最終ログイン日時を保存するコードを追加します。

public function handle($event)
{
    $event->user->last_login_at = now();
    $event->user->save();
}

これで完了です。

最終ログアウト日時を保存する方法

ログイン情報を保存する場合と少し違っている部分があるので、こちらも2つの方法を紹介します。

まずはログイン時と同じくテーブルにlast_logout_atというフィールドを追加します。

Schema::create('users', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name');
    $table->string('email')->unique();
    $table->string('password');
    $table->dateTime('last_login_at')->nullable();
    $table->dateTime('last_logout_at')->nullable(); // ←追加
    $table->rememberToken();
    $table->timestamps();
});
php artisan migrate:fresh --seed

テーブル構造はこうなります。

LoginControllerで実装する方法

最終ログイン保存のときと同じくLoginController.phpの中でlogout()というメソッドをオーバーライドしますが、少しアプローチが異なっています。

なぜなら、トレイトは同じメソッドを作ってしまうと上書きされて消滅してしまうため、logout()をオーバーライドしてしまうと、本来必要なログアウトの手順が実行されなくなってしまうためです。

かと言ってloggedOut()を使っても、こちらはログアウト後なのでユーザー情報を引き出すことはできません。

もちろん、以下のようにAuthenticatesUserslogout()内の記述をそっくりそのままコピーして必要なコードを足すという方法も無くはないですが、今後Laravelがアップデートされた場合に記述が変わってしまう可能性があるため、これもあまりおすすめできません。

public function logout(Request $request)
{
    // ここにコードを足す(おすすめしない方法です!)

    $this->guard()->logout();

    $request->session()->invalidate();

    return $this->loggedOut($request) ?: redirect('/');
}

ではこれを解決するにはどうするかというと、asを使って名前を変更すればOKです。実際の例を見てみましょう。

class LoginController extends Controller
{
    use AuthenticatesUsers { logout as originalLogout; }

    // 省略

    public function logout(Request $request)
    {
        $user = $request->user();
        $user->last_logout_at = now();
        $user->save();

        return $this->originalLogout($request); // 元々のログアウト
    }

}

やっていることは、もともとのlogout()メソッドの名前をoriginalLogout()に変更し、新しく追加したlogout()内で、このメソッドを呼び出して本来のログアウト処理を実行しています。

そして、もちろん新logout()の中では、ログアウト直前に最終ログアウト日時を保存するコードを追加しています。

ログアウト・イベントで実装する

イベントを使って最終ログアウト日時を保存するには、ログインの場合と同じくイベントの登録が必要になります。

app/Providers/EventServiceProvider.phpを開いてログアウト・イベントを登録します。

protected $listen = [
    // ログアウトが成功したら実行
    'Illuminate\Auth\Events\Logout' => [
        'App\Listeners\LogSuccessfulLogout',
    ],

そして、以下のコマンドでイベント・リスナーLogSuccessfulLogoutを作成しましょう。

php artisan event:generate

最後に、作成したapp/Listeners/LogSuccessfullLogout.phphandle()内で同じくログアウト日時を保存する記述をすればOKです。

public function handle(Logout $event)
{
    $event->user->last_logout_at = now();
    $event->user->save();
}

※ ちなみに、LogSuccessfulLogoutイベントは実際にログアウトした後でもユーザー情報は残してくれているので、上記のように保存が可能になっています。

今回使ったファイルをダウンロード

以下から、今回の記事で実際に使ったLoginControllerLogAuthenticatedLogSuccessfulLogoutのファイルをダウンロードできます。

※ ただし、マイグレーションやイベントの登録はこの記事を参考にしてご自身で行う必要があります。また、全てのファイルを実装すると二重でログイン&ログアウト情報が保存されることになりますので、こちらもどちらかご自身で選択して実装してください。

最終ログイン&ログアウト日時を保存するソースコード