開発ブログ

株式会社Nextatのスタッフがお送りする技術コラムメインのブログ。

電話でのお問合わせ 075-744-6842 ([月]-[金] 10:00〜17:00)

  1. top >
  2. 開発ブログ >
  3. PHP >
  4. Laravel >
  5. Laravel5.5 カスタムGuardでログイン時にEgerLoadする

Laravel5.5 カスタムGuardでログイン時にEgerLoadする

こんにちは。
ニシザワです。


今回はLaravelのCutomGuardでログイン時にEgerLoadする方法を書いていきます。
例えば以下のような関係のモデルがあるとします。
<?php
declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

/**
 * Class User
 * @package App\Models\Masterid
 * @property int $id ID
 * @property int $organization_id 組織ID
 * @property string $name 名前
 * @property string $passward パスワード
 * @property string $email メールアドレス
 *
 * @property Organization $organization 組織取得
 */
class User extends Authenticatable
{
    use Notifiable;

    /**
     * 組織取得
     * @return BelongsTo
     */
    public function organization() : BelongsTo
    {
        return $this->belongsTo(Organization::class);
    }

}
<?php
declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

/**
 * Class Organization
 * @package App\Models\Master
 * @property int $id ID
 * @property string $name 会社名
 */
class Organization extends Model
{

}
ユーザー認証を使うアプリケーションにおいて、Userを取ってきて、
$user->organization
なんてことをすることが多いかと思います。
ここでEgerLoadしていないと毎回queryが走ってしまうので、N+1問題が発生します。
なるべく、同じqueryを発行したくないので今回はその場合の、対処をCustomGuardを使って実装していきます。

CutomGuardを作成する

まずは、app/auth以下にCustomGaurd.phpを作成し今回はSessionGuardを拡張して下記の通り記述します。
<?php

namespace App\Auth;

use App\Models\Master\FiscalYear;
use App\Models\Master\User;
use Carbon\Carbon;
use Illuminate\Auth\SessionGuard;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;

/**
 * Class CustomSessionGuard
 * @package App\Auth
 */
class CustomGuard extends SessionGuard
{

    /**
     * @var \Illuminate\Contracts\Auth\Authenticatable|User
     */
    protected $user;

    /**
     * 認証ユーザー取得時に関係情報を取得しておく
     * @return User|AuthenticatableContract|null
     */
    public function user()
    {
        parent::user();
        if(is_null($this->user)){
            return null;
        }
        if(!$this->user->relationLoaded('organization')){
            $this->user->setRelation("organization", $this->user->organization);
        }
        return $this->user;
    }

}
ログインしているユーザーの取得はSessionGuardのuserメソッドで実装されているのでここを拡張して上げれば問題ないです。
今回は1対1のリレーションなんで手動EgerLoadで行います。

CutomGuardの登録

まずはconfig/auth.phpにカスタムドライバーを登録します。
'guards' => [
        'web' => [
            'driver' => 'custom',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],
次にapp/Providers/AuthServiceProvider.phpを作成します。
 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Auth::extend('custom', function($app, $name, array $config) {

            return new CustomGuard($name,Auth::createUserProvider($config['provider']), $app["session.store"]);
        });
    }
}
ここで最初に作ったCustomGuardを登録しています。 最後にconfig/app.phpにプロバイダー登録すれば完了です。


    'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        Illuminate\Cache\CacheServiceProvider::class,
        Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
        Illuminate\Cookie\CookieServiceProvider::class,
        Illuminate\Database\DatabaseServiceProvider::class,
        Illuminate\Encryption\EncryptionServiceProvider::class,
        Illuminate\Filesystem\FilesystemServiceProvider::class,
        Illuminate\Foundation\Providers\FoundationServiceProvider::class,
        Illuminate\Hashing\HashServiceProvider::class,
        Illuminate\Mail\MailServiceProvider::class,
        Illuminate\Notifications\NotificationServiceProvider::class,
        Illuminate\Pagination\PaginationServiceProvider::class,
        Illuminate\Pipeline\PipelineServiceProvider::class,
        Illuminate\Queue\QueueServiceProvider::class,
        Illuminate\Redis\RedisServiceProvider::class,
        Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
        Illuminate\Session\SessionServiceProvider::class,
        Illuminate\Translation\TranslationServiceProvider::class,
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,

        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class, //←ここ
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

    ],
これで、Guard経由のユーザー取得には常にorganizationの情報が一緒についてくる状態となりました。
微々たる、queryの時間ですが、他にもいろいろとユーザーに紐づけている場合は、結構な量になりますので、ぜひ試してみてください。
TOPに戻る