Laravel11でマルチログインの実装

Laravel11でマルチログインを実装するためにやるべきことをメモ書き代わりに記載。

  • マイグレーション
    • モデルとマイグレーションファイルの作成
    • 既存UserモデルとUserマイグレーションファイルの内容をコピーして整える
  • ガードとプロバイダー設定(config/auth.php)
    • 認証関連の設定
  • ルート設定
    • admin.phpを作成し、web.phpとauth.phpの内容をコピペして修正
  • ミドルウェア設定
    • 未認証、認証済み時のリダイレクト先の設定
  • LoginRequest設定
    • 入力したMailアドレスとPasswordが正しいかをDBに問い合わせてチェックする機能の提供
    • ルートを見て処理を分ける
  • セッション設定
    • adminとuserでそれぞれ異なるセッションで管理できるように設定
  • コントローラーとブレードをコピペして作成
    • Userのコントローラーとブレード一式をコピペして、Admin用のコントローラーとブレードを作成する

マイグレーション

adminのモデルとマイグレーションを作成する。既存のUserのファイルの内容をコピーして名前をadminなどに変更。

php artisan make:model Admin -m
// -m オプションをつけるとマイグレーションファイルも一緒に作成してくれる

作成後、以下のコマンドで反映する

php artisan migrate

ガードとプロバイダー設定(config/auth.php)

config/auth.phpで以下のようにadminのガードを設定

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'user' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
    ],

以下のようにprovidersにadminsを追加

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],
    ],

ルート設定

route/admin.phpを作成して、route/web.phpとroute/auth.phpの内容をコピーして、必要なところを書き換える

下記のようにviewの先頭にadmin/とつけておく。後でviewsの配下にadminのフォルダを作成して関連ブレードファイルをuserからコピーして作成。

※userフォルダも作成して、既存のファイルを入れておく

Route::get('/', function () {
    return view('admin/welcome');
});

下記のように認証関連の部分のミドルウェアの設定をmiddleware(‘auth:admin’)middleware(‘guest:admin’)などに書き換える。設定内容はガード設定と一致する必要がある。

※authはIlluminate\Auth\Middleware\Authenticateのエリアス
※guestはIlluminate\Auth\Middleware\RedirectIfAuthenticatedのエリアス

Route::middleware('auth:admin')->group(function () {
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});

作成したadmin.phpルート設定を反映するのに、bootstrap/app.phpの設定を追加する。then:の中で追加。

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__ . '/../routes/web.php',
        commands: __DIR__ . '/../routes/console.php',
        health: '/up',
        then: function () {
            Route::middleware('web')
                ->prefix('admin')
                ->name('admin.')
                ->group(base_path('routes/admin.php'));
        },
    )
    ->withMiddleware(function (Middleware $middleware) {
        //
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

ミドルウェア設定

app/Http/Requests/Providers/AppServiceProvider.phpのbootメソッド(など)で未認証時のリダイレクト先と認証済みのリダイレクト先の設定

※Laravel11ではMiddlewareディレクトリがVender以下に移動され、AuthenticateやRedirectIfAuthenticatedで直接書き換えないで、リダイレクト先をredirectUsingメソッドを通じて行う

※AppServiceProvider.bootメソッドはHTTPリクエストの度に呼ばれる

use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Auth\Middleware\RedirectIfAuthenticated;    

    public function boot(): void
    {
        // 未認証時のリダイレクト先
        Authenticate::redirectUsing(function () {
            if (Route::is('admin.*')) {
                return route('admin.login');
            } else {
                return route('login');
            }
        });

        // 認証済みのリダイレクト先
        RedirectIfAuthenticated::redirectUsing(function () {
            if (Route::is('admin.*')) {
                return route('admin.dashboard');
            } else {
                return route('dashboard');
            }
        });
    }

リクエストクラス設定(LoginRequest)

App\Http\Requests\Auth\LoginRequest.phpを編集する。
ログインリクエストでEmailとPasswordを照合して、問題がある時に弾く処理。

    public function authenticate(): void
    {
        $this->ensureIsNotRateLimited();

        $attempt = function ($guard) {
            if (!Auth::guard($guard)->attempt($this->only('email', 'password'), $this->boolean('remember'))) {
                RateLimiter::hit($this->throttleKey());

                throw ValidationException::withMessages([
                    'email' => trans('auth.failed'),
                ]);
            }
        };

        if ($this->routeIs('admin.*')) {
            $attempt('admin');
        } else {
            $attempt('user');
        }

        RateLimiter::clear($this->throttleKey());
    }

セッション設定

.envファイルに以下のようにセッションのクッキー名を定義する

SESSION_COOKIE="${APP_NAME}_session_user"
SESSION_COOKIE_ADMIN="${APP_NAME}_session_admin"

config/session.phpの設定

    'cookie' => env(
        'SESSION_COOKIE',
        Str::slug(env('APP_NAME', 'laravel'), '_') . '_session_user'
    ),
    'cookie_admin' => env(
        'SESSION_COOKIE_ADMIN',
        Str::slug(env('APP_NAME', 'laravel'), '_') . '_session_admin'
    ),

AppServiceProvider.bootメソッドで以下のように設定

// セッションの切り替え
if (request()->is('admin*')) {
    config(['session.cookie' => config('session.cookie_admin')]);
}

if (request()->is('owner*')) {
    config(['session.cookie' => config('session.cookie_owner')]);
}

コントローラーとブレード作成

adminファイルを作成してUserのコントローラーとブレードをコピペして放り込む

コントローラー設定

AuthenticatedSessionControllerの修正点

    public function create(): View
    {
        return view('admin.auth.login');
    }

    public function store(LoginRequest $request): RedirectResponse
    {
        $request->authenticate();

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

        return redirect()->intended(route('admin.dashboard', absolute: false));
    }

    public function destroy(Request $request): RedirectResponse
    {
        Auth::guard('admin')->logout();

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

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

        return redirect('/admin');
    }

RegisteredUserControllerの修正点

    public function create(): View
    {
        return view('admin.auth.register');
    }

    public function store(Request $request): RedirectResponse
    {
        $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:' . Admin::class],
            'password' => ['required', 'confirmed', Rules\Password::defaults()],
        ]);

        $user = Admin::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        event(new Registered($user));

        Auth::login($user);

        return redirect(route('admin.dashboard', absolute: false));
    }

PasswordResetLinkControllerの修正点

    public function create(): View
    {
        return view('admin.auth.forgot-password');
    }

ProfileController.phpの修正点

    public function edit(Request $request): View
    {
        return view('admin.profile.edit', [
            'user' => $request->user(),
        ]);
    }

    public function update(ProfileUpdateRequest $request): RedirectResponse
    {
        $request->user()->fill($request->validated());

        if ($request->user()->isDirty('email')) {
            $request->user()->email_verified_at = null;
        }

        $request->user()->save();

        return Redirect::route('admin.profile.edit')->with('status', 'profile-updated');
    }

    /**
     * Delete the user's account.
     */
    public function destroy(Request $request): RedirectResponse
    {
        $request->validateWithBag('userDeletion', [
            'password' => ['required', 'current_password'],
        ]);

        $user = $request->user();

        Auth::logout();

        $user->delete();

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

        return Redirect::to('/admin');
    }

ブレード設定

route(‘admin.login’)など、adminの接頭詞をつける。接頭詞はbootstrap/app.php以下の設定と一致する必要がある。

対象ファイル:

  • login.blade.php
  • register.blade.php
  • profile/
    • edit.blade.php
    • partials/
      • delete-user-form.blade.php
      • update-password-form.blade.php
      • update-profile-information-form.blade.php
        then: function () {
            Route::middleware('web')
                ->prefix('admin')
                ->name('admin.')
                ->group(base_path('routes/admin.php'));
        },

コメント

タイトルとURLをコピーしました