Laravel12　マルチログイン機能の実装

## Laravel12にてマルチログイン機能を実装した際の手順の忘備録

### 概要
Laravel12にてマルチログイン実装を行なったので忘備録として記録を残す

### 前提条件
①ローカル環境上でdocker-composeにて以下の構成になるようにコンテナを作成しています。

言語 php
RDBMS mysql
webサーバーソフトウェア nginx
メールサーバーあり
redisあり

※今回の投稿にて、各ソフトウェアのバージョンは無関係と認識しているので割愛します。

②認証機能についてはLaravel/Breezeを使用しており、Laravel/permissionも導入済みです。

### config/auth.phpの設定

以下の認証に関する設定箇所に、認証方法を追加する。
コードを追加した後は追加コードを適用させるために、`php artisan config:cache`を実行。

```
// 既存コード ▼
'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
    ],


'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => env('AUTH_MODEL', App\Models\User::class),
    ],



// 修正後コード ▼
'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admin' => [ // 管理者用
            'driver' => 'session',
            'provider' => 'admins',
        ], 
        'customer' => [ // 一般受付用
            'driver' => 'session',
            'provider' => 'customers',
        ],
    ],


'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => env('AUTH_MODEL', App\Models\User::class),
        ],
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],
        'customers' => [
            'driver' => 'eloquent',
            'model' => App\Models\Customer::class,
        ],
    ],
```

### auth.phpの中身をカスタムする
```
/**
 * Admin
 */
Route::prefix('admin')->name('admin.')->middleware('guest:admin')->group(function () {
    Route::get('login', [App\Http\Controllers\Admin\Auth\AuthenticatedSessionController::class, 'create'])->name('login');
    Route::post('login', [App\Http\Controllers\Admin\Auth\AuthenticatedSessionController::class, 'store']);
    Route::get('register', [App\Http\Controllers\Admin\Auth\RegisteredUserController::class, 'create'])->name('register');
    Route::post('register', [App\Http\Controllers\Admin\Auth\RegisteredUserController::class, 'store']);
    Route::get('forgot-password', [App\Http\Controllers\Admin\Auth\PasswordResetLinkController::class, 'create'])->name('password.request');
    Route::post('forgot-password', [App\Http\Controllers\Admin\Auth\PasswordResetLinkController::class, 'store'])->name('password.email');
    Route::get('reset-password/{token}', [App\Http\Controllers\Admin\Auth\NewPasswordController::class, 'create'])->name('password.reset');
    Route::post('reset-password', [App\Http\Controllers\Admin\Auth\NewPasswordController::class, 'store'])->name('password.store');
});

Route::prefix('admin')->name('admin.')->middleware(['auth:admin'])->group(function () {
    Route::get('/dashboard', function () {return view('admin.dashboard');})->name('dashboard');
    Route::post('logout', [App\Http\Controllers\Admin\Auth\AuthenticatedSessionController::class, 'destroy'])->name('logout');
});
```

上記の様な構成でcustomer側についても作成をお願いします。

### Migrateion & Modelファイルの作成を実施
以下のコマンドでModelとMigrationファイルの作成を行う。

`
php artisan make:model Admin --migration
php artisan make:model Customer --migration
`

Laravelのユーザー認証機能を使うために「Model」ではなく「Authenticatable」を継承する事。
```
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable; // ここを追加
use Illuminate\Database\Eloquent\SoftDeletes;

class Admin extends Authenticatable // Authenticatableに変更
{
    use SoftDeletes;

    protected $guard_name = 'admin';
    
    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}

```
usersのマイグレーションファイルを踏襲して作成 ▼
```
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
            $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('admins');
    }
};

```

customerについてもconfig/auth.phpにて設定しているので、上記と同様にファイルの作成を実施ください。

### Migrationファイルの内容に沿ったSeederファイルを作成


以下のコマンドでSeederファイルを作成する
`
php artisan make:seeder AdminSeeder
`
コマンドの実行後は以下を例としてデータ作成のコードを実装して、`php artisan db:seed --class=AdminSeeder`でadminsテーブルの中にレコードを作成して下さい。
またはドキュメントの[「追加シーダーの呼び出し」項目](https://readouble.com/laravel/12.x/ja/seeding.html)を参考にDatabaseSeederファイルの中身を改良して他Seederについても実行を行うことが可能です
```
<?php

namespace Database\Seeders;

use App\Models\Admin;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;

class AdminSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $admins = [
            [
                'name' => '管理者',
                'password' => Hash::make('Password123+'),
                'email' => 'admin@test.com',
            ],[
                'name' => 'スタッフ',
                'password' => Hash::make('Password123+'),
                'email' => 'staff@test.com',
            ]
        ];

        foreach ($admins as $admin) {
            Admin::create($admin);
        }
    }
}


```

customerのseederファイルについても同様に作成いただいて問題ないです。

### 描画ファイルの追加
Admin用とCustomer用にbladeファイルを用意します。
手順は簡単で‥
①「admin」をresources/viewsディレクトリの配下に作成する
②laravel/breezeのインストール時に追加されたresources/views/authディレクトリとdashboard.blade.phpを、上記adminディレクトリに追加
③layoutsディレクトリを上記adminディレクトリに追加
④app/View/Componentsディレクトリの中に「admin」と「customer」ディレクトリを作成する
⑤AppLayout.phpとGuestLayout.phpファイルを上記adminディレクトリにコピーして、ネームスペースとview()の引数をadminに調整する
⑥dashboard.blade.phpのlayoutコンポーネントの記述をadmin用に調整
⑦各種bladeファイルのrouteヘルパー関数の記述をadmin用にセッティングして下さい。
⑧上記①~⑦の内容をcustomer側にも展開します。

### bootstrap/app.phpの中に認証リダイレクト設定の記述を追加

認証のリダイレクト設定についてはLaravel12ではapp.phpにて設定することになっているので以下の通り定義を行います。本来であれば、middlewareのエイリアス登録を行う箇所として使用する事が望ましそうなので、じゃんじゃんmiddlewareを作成する予定の場合はMiddlewareファイルの作成を行なって分離すると良いかもです。

```
->withMiddleware(function (Middleware $middleware) {
        // 未認証ユーザーのリダイレクト　先を制御
        $middleware->redirectGuestsTo(function (Request $request) {
            return match (true) {
                $request->is('admin/*')    => route('admin.login'),
                $request->is('customer/*') => route('customer.login'),
            };
        });
    
        // 認証ユーザーのリダイレクト先を制御
        $middleware->redirectUsersTo(function (Request $request) {
            return match (true) {
                $request->is('admin/*')    => route('admin.dashboard'),
                $request->is('customer/*') => route('customer.dashboard'),
            };
        });
    })
````

### ログイン処理を調整
AuthenticatedSessionControllerファイルの中のstoreメソッドについて標準の記述から以下の内容に調整して下さい。

```
$credentials = $request->validated();

        if (Auth::guard('admin')->attempt($credentials, $request->filled('remember'))) {
            $request->session()->regenerate();
            return redirect()->intended(route('admin.dashboard'));
        }

        return back()->withErrors([
            'email' => 'ログインに失敗しました。',
        ]);
```
 要はguardに沿って認証を行うって事ですね

### 検証
検証の項目は以下の通りです。
✅seeder内容に沿ってログインが実施できるか
✅認証済みの状態で、login画面にアクセスするとダッシュボードにリダイレクトされるか
✅ログアウト操作が問題なく実行できるか
✅未認証の状態でログイン操作を実施した際に、login画面にリダイレクトが実施されるか

ここまできたら、マルチログインの大半は完了しているので残りもカスタマイズを行い自分流のwebアプリにしていきましょう。
ここまでご確認いただきありがとうございました。