RitoLabo

Laravelの認証ログイン時にデータベースへ最終ログイン日時の書き込みを行う

  • 公開:
  • 更新:
  • カテゴリ: PHP Laravel
  • タグ: PHP,Laravel,migration,5.5,5.4,5.3,ServiceProviders,Events,Listeners,Auth,5.6

LaravelのAuth認証機能を使うと、ログインなどのユーザ管理機能を素早く導入できますが、その機能と情報は必要最小限になっています。

ただし、Laravelの認証機能は固定化された機能ではなく、都度必要な機能を拡張することが可能です。

という事で今回は、管理機能でもよく使われる、ログインする毎にユーザ情報へ最終ログインの記録を行う機能を実装します。

アジェンダ
  1. 開発環境
  2. 最終ログインカラムの作成
  3. イベントリスナの作成
  4. ログインコントローラでの発火処理

開発環境

今回の開発環境は以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • MySQL 5.7
  • PHP 7.1/7.2
  • Laravel

Laravelのバージョンに関しては5.6/5.5/5.4/5.3にて動作確認済みです。

また、laravelのソースディレクトリを「laravel/」とします。

基本的な認証機能は導入済みである前提で進めますので、導入までに関しては
Laravelの認証機能でログイン/ユーザ登録/パスワードリセットなどの管理画面を一撃構築する(基本&入門編)
を確認してください。

最終ログインカラムの作成

まずは最終ログイン日時を記録するためのカラムをusersテーブルに作成します。

laravelルートディレクトリへ移動し、以下のartisanコマンドを叩いてマイグレーションファイルを生成します。

# Laravelルートディレクトリへ移動する
cd /path/to/laravel

# artisanコマンドでマイグレーションファイルを生成する
php artisan make:migration add_column_last_login_at_users_table --table=users

# 実行結果
[deom@localhost laravel]# php artisan make:migration add_column_last_login_at_users_table --table=users
Created Migration: 2019_04_14_133657_add_column_last_login_at_users_table

マイグレーションファイルは laravel/database/migrations 配下に生成されます。

laravel
├─ database
   ├─ migrations
      ├─ 2014_10_12_000000_create_users_table.php
      ├─ 2014_10_12_100000_create_password_resets_table.php
      └─ 2019_04_14_133657_add_column_last_login_at_users_table.php
laravel/database/migrations/2019_04_14_133657_add_column_last_login_at_users_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddColumnLastLoginAtUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
//
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
//
});
}
}

生成したマイグレーションファイルを、以下のように記述します。

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddColumnLastLoginAtUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->timestamp('last_login_at')->nullable()->after('remember_token')->comment('最終ログイン');
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('last_login_at');
});
}
}

最終ログインを格納するカラム名をlast_login_atとし、nullを許容するtimestamp型として、remember_tokenカラムの後ろに作成する記述になっています。

それでは、マイグレーションを実行し、テーブルにカラムを追加します。

# Laravelルートディレクトリへ移動する
cd /path/to/laravel

# artisanコマンドでマイグレーションを実行する
php artisan migrate

# 実行結果
[demo@localhost laravel]# php artisan migrate
Migrating: 2019_04_14_133657_add_column_last_login_at_users_table
Migrated: 2019_04_14_133657_add_column_last_login_at_users_table

usersテーブルにlast_login_atカラムが追加されました。

mysql> show columns from users;
+----------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| email | varchar(255) | NO | UNI | NULL | |
| password | varchar(255) | NO | | NULL | |
| remember_token | varchar(100) | YES | | NULL | |
|
last_login_at | timestamp | YES | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+----------------+------------------+------+-----+---------+----------------+

イベントリスナの作成

ここからは最終ログインを書き込む処理をLaravelに実装していきますが、イベント&リスナを使って進めていきます。

まずは、イベントとリスナを登録します。EventServiceProvider.phpに以下を追記します。

laravel/app/Providers/EventServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'App\Events\Event' => [
'App\Listeners\EventListener',
],
// ログイン時にイベントを発行
'App\Events\Logined' => [
// 最終ログインを記録するリスナー
'App\Listeners\LastLoginListener',
],
];

/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
parent::boot();

//
}
}

ログイン時にイベントを発行するLoginedイベントと、最終ログインを記録するLastLoginリスナーを登録しています。

Laravelルートディレクトリへ移動し、以下のartisanコマンドを叩き、イベントとリスナファイルを生成します。

# laravelルートディレクトリへ移動
cd /path/to/laravel

# artisanコマンドでイベントとリスナファイルを生成する
php artisan event:generate

# 実行結果
[demo@localhost laravel]# php artisan event:generate
Events and listeners generated successfully!

生成されたファイルはそれぞれ、laravel/app/Eventslaravel/app/Listeners に配置されます。

laravel
├─ app
│ ├─ Events
│ │ ├─
Logined.php
│ │ └─ Event.php
│ ├─ Listeners
│ │ ├─ EventListener.php
│ │ └─
LastLoginListener.php

今回、イベントクラスに関しては今回は生成されたままで追記は行いません。リスナクラスには、最終ログインを記録する処理をhandle()メソッドに記述しています。

laravel/app/Events/Logined.php
<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

/**
* ログイン完了時に発火するイベントクラス
* Class Logined
* @package App\Events
*/
class Logined
{
use Dispatchable, InteractsWithSockets, SerializesModels;

/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}

/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
laravel/app/Listeners/LastLoginListener.php
<?php

namespace App\Listeners;

use App\Events\Logined;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;

/**
* ログイン完了時に発火するイベントをキャッチするリスナークラス
* Class LastLoginListener
* @package App\Listeners
*/
class LastLoginListener
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}

/**
* Handle the event.
*
* @param Logined $event
* @return void
*/
public function handle(Logined $event)
{
$user = Auth::user();
$user->last_login_at = Carbon::now();
$user->save();
}
}

ログインコントローラでの発火処理

最後に、ログイン認証後にイベントを発火させる記述を行います。

Laravelの認証機能ではLoginControllerがログイン処理を担っているので、ここに実装していきます。

laravel/app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\Events\Logined;

class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/

use AuthenticatesUsers;

/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/account';

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

/**
* ログイン認証後の処理
* @param Request $request
* @param $user
*/
protected function authenticated(Request $request, $user)
{
// ログインイベントを発火させ最終ログイン日時を記録する
event(new Logined());
}
}

AuthenticatesUsersトレイトのauthenticated()メソッドをオーバーライドしている形になります。これによってログイン認証後にイベントが発火し、最終ログイン日時がデータベースへ更新されます。

動作確認

それでは動作確認を行ってみます。
http://YOUR-DOMAIN/login
から実際にログインを行い、最終ログイン日時が更新されるか確認します。

mysql> select id, name, last_login_at from users;
+----+--------+---------------------+
| id | name | last_login_at |
+----+--------+---------------------+
| 1 | user01 |
2018-01-14 14:08:48 |
| 2 | user02 | NULL |
| 3 | user03 | NULL |
| 4 | user04 | NULL |
| 5 | user05 | NULL |
| 6 | user06 | NULL |
| 7 | user07 | NULL |
| 8 | user08 | NULL |
| 9 | user09 | NULL |
| 10 | user10 | NULL |
+----+--------+---------------------+

問題なく最終ログイン日時が更新されました。

まとめ

以上で作業は完了となります。
Laravelの認証機能回りは、トレイトの形で色々な処理が実装されていますが、オーバーライドすればコントローラ内で認証過程の処理を実装する事が出来るので覚えておくと便利です。

また、vendor配下のファイルに直接手を入れる場合、Githubなどでソース管理を行っていると除外されていてコミットされないので注意が必要です。そのためにも、組み込み機能のソースには手を付けず、出来るだけ組み込まれている処理についてはオーバーライドで実装するようにしましょう。