「ユーザー認証」のポストは、もうすでに11回目になりました。Taylorくんのプログラムは、宝石がいっぱい詰まっているから、ソースコードを見ているといろいろ発見あります。

例えば、私はユーザー認証成功後にログインの記録が欲しいです。つまり、ユーザーがどのIPからどのブラウザあるいはどのOSでアクセスしたかをDBに記録したいのです。ソースコードを見ているとそのことをあたかも考慮しているメカニズムが存在することに気づきます。今回はそれをどう利用するかを紹介します。

まず、ユーザー認証(9)Laravel 5.2 コンポーネント自動作成で紹介したコマンドで作成されるファイルの1つ、AuthController.phpを編集することになります。しかし、以下を見てわかるようにトレイトをバンバン使っているので、そのファイルの中身はほとんど空です。

namespace App\Http\Controllers\Auth;

use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;

class AuthController extends Controller
{
    use AuthenticatesAndRegistersUsers, ThrottlesLogins;

    protected $redirectTo = '/';

    public function __construct()
    {
        $this->middleware('guest', ['except' => 'logout']);
    }

    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|confirmed|min:6',
        ]);
    }

    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }
}

さて、どうすればよいでしょう?

上の11行目のトレイトのファイルを遡って辿ります。

AuthenticatesAndRegistersUsers

AuthenticatesUsers

このファイルで、postLoginは、ログイン情報を送信したときに呼ばれる関数、その定義の中では、loginをコールしています。

...
    public function postLogin(Request $request)
    {
        return $this->login($request);
    }
...
    public function login(Request $request)
    {
        $this->validate($request, [
            $this->loginUsername() => 'required', 'password' => 'required',
        ]);
 
        $throttles = $this->isUsingThrottlesLoginsTrait();

        if ($throttles && $this->hasTooManyLoginAttempts($request)) {
            return $this->sendLockoutResponse($request);
        }

        $credentials = $this->getCredentials($request);

        if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
            return $this->handleUserWasAuthenticated($request, $throttles);
        }

       if ($throttles) {
            $this->incrementLoginAttempts($request);
        }
        return $this->sendFailedLoginResponse($request);
    }
...
    protected function handleUserWasAuthenticated(Request $request, $throttles)
    {
        if ($throttles) {
            $this->clearLoginAttempts($request);
        }
        if (method_exists($this, 'authenticated')) {
            return $this->authenticated($request, Auth::guard($this->getGuard())->user());
        }
        return redirect()->intended($this->redirectPath());
    }
...

loginの定義ではattempt()(21行目)で認証を実行しています。そしてそれが成功なら、handleUserWasAuthenticatedをコールします。

今度は、36行目見てください。ここのifは、このクラスにauthenticatedというメソッドが定義されているなら、それを実行しますよ!という意味です。

先のAuthController.phpに以下のメソッドを追加します。以下のハイライトされた
部分です。

namespace App\Http\Controllers\Auth;

use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;

use App\UserLog;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    use AuthenticatesAndRegistersUsers, ThrottlesLogins;

    protected $redirectTo = '/';

    public function __construct()
    {
        $this->middleware('guest', ['except' => 'logout']);
    }

    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|confirmed|min:6',
        ]);
    }

    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }

    public function authenticated(Request $request, User $user)
    {
        UserLog::add($request, $user);//ここでログを作成

        return redirect()->intended($this->redirectPath());
    }
}

これで、ログインが成功するたびに、このauthenticatedがコールされログインの履歴をDBに作成します。

以下はそこで使用されるモデルUserLogです。ホスト名を得るために、ミューテーターを使用します。


namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;

class UserLog extends Model {

    protected $table = 'user_log';
    protected $primaryKey = null;
    public $incrementing = false;
    public $timestamps = true;

    public function setHostnameAttribute($ip)
    {
        $this->attributes['hostname'] = gethostbyaddr($ip);
    }

    public static function add(Request $request, User $user)
    {
        $userLog = new static;

        $userLog->user_id = $user->id;
        $userLog->ip = $request->getClientIp();
        $userLog->hostname = $userLog->ip; // ミューテーターを使用

        $userLog->save();
    }
}

user_logのDBテーブルを作成することをお忘れずに。

authenticatedには、ログインの履歴を作成するだけでなく、会員の1周年記念のためのお祝いのメッセージや、それにまつわる特典の提供とか、いろいろなコードを含むことが考えられます。

By khino