Laravel マルチログイン設定

マルチログイン流れ

  1. モデル、マイグレーション作成
  2. ルート設定
  3. ルートサービスプロバイダ設定
  4. ガード設定
  5. ミドルウェア設定
  6. リクエストクラス設定
  7. コントローラー&ブレード作成

7. コントローラー&ブレード作成

1.モデル&マイグレーション設定

-mオプションでマイグレーションとモデルを同時に作成できる。
app/models配下にモデルファイルが生成される

既存のuser.phpの内容(use文とクラス内容)をowner,adminそれぞれにコピーする

マイグレーションファイルもusers_tableのupメソッドの中身の$tableをowner,adminそれぞれにコピーする

全てできたらphp artisan migrate

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable
{
    use HasFactory;

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

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

adminのマイグレーションファイル

<?php

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

class CreateAdminsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        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();
        });
    }

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

次にパスワードリセットテーブルを作成する。
owner_passwarod_resetsadmin_password_resetsテーブル作成
$ php artisan make:migration create_テーブル名

最初からあるuserのpassword_resets_tableのupメソッド内をコピーしてadmin,ownerそれぞれのマイグレーションファイルへ貼り付ける
貼り付けたらphp artisan migrate

<?php

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

class CreateAdminPasswordResets extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('admin_password_resets', function (Blueprint $table) {
            $table->string('email')->index();
            $table->string('token');
            $table->timestamp('created_at')->nullable();
        });
    }

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

2. ルート設定

bleezeをいれるとweb.php,auth.phpが追加されている
Userで使ってるのはweb.phpauth.php

Owner用の routes/owner.php, Admin用の routes/admin.php をそれぞれエディタから作成する
web.phpの中身を全部コピーして貼り付ける
require __DIR__.'/auth.php’;をけしてauth.phpの中身をすべてはりつける

use文を上にもっていき、controllers配下にそれぞれuser,admin,ownerでフォルダを作成するので書き加える

admin.php
use App\Http\Controllers\Admin\Auth\AuthenticatedSessionController;

auth.php
use App\Http\Controllers\User\Auth\AuthenticatedSessionController;

owner.php
use App\Http\Controllers\Owner\Auth\AuthenticatedSessionController;

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Admin\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Admin\Auth\ConfirmablePasswordController;
use App\Http\Controllers\Admin\Auth\EmailVerificationNotificationController;
use App\Http\Controllers\Admin\Auth\EmailVerificationPromptController;
use App\Http\Controllers\Admin\Auth\NewPasswordController;
use App\Http\Controllers\Admin\Auth\PasswordResetLinkController;
use App\Http\Controllers\Admin\Auth\RegisteredUserController;
use App\Http\Controllers\Admin\Auth\VerifyEmailController;

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

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth'])->name('dashboard');

Route::middleware('guest')->group(function () {
    Route::get('register', [RegisteredUserController::class, 'create'])
                ->name('register');

    Route::post('register', [RegisteredUserController::class, 'store']);

    Route::get('login', [AuthenticatedSessionController::class, 'create'])
                ->name('login');

    Route::post('login', [AuthenticatedSessionController::class, 'store']);

    Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])
                ->name('password.request');

    Route::post('forgot-password', [PasswordResetLinkController::class, 'store'])
                ->name('password.email');

    Route::get('reset-password/{token}', [NewPasswordController::class, 'create'])
                ->name('password.reset');

    Route::post('reset-password', [NewPasswordController::class, 'store'])
                ->name('password.update');
});

Route::middleware('auth')->group(function () {
    Route::get('verify-email', [EmailVerificationPromptController::class, '__invoke'])
                ->name('verification.notice');

    Route::get('verify-email/{id}/{hash}', [VerifyEmailController::class, '__invoke'])
                ->middleware(['signed', 'throttle:6,1'])
                ->name('verification.verify');

    Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store'])
                ->middleware('throttle:6,1')
                ->name('verification.send');

    Route::get('confirm-password', [ConfirmablePasswordController::class, 'show'])
                ->name('password.confirm');

    Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']);

    Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])
                ->name('logout');
});

3.ルートサービスプロバイダ設定

画面を読み込むたびに実行される内容

定数としてOwner, AdminそれぞれホームURLを設定

public const OWNER_HOME = ‘/owner/dashboard’
public const ADMIN_HOME = ‘/admin/dashboard’

Userがログインしたらdashboradにリダイレクトがかかる

Owner,Adminそれぞれログインしたらリダイレクト先の設定

Bootメソッドにuser, owner, adminのURLを追記 Prefixをつける, as()で名前付きルートにもprefixつける

<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * The path to the "home" route for your application.
     *
     * This is used by Laravel authentication to redirect users after login.
     * User,Admin,Ownerそれぞれのログイン後のリダイレクト先パスの設定
     * @var string
     */
    public const HOME = '/dashboard';
    public const OWNER_HOME = '/owner/dashboard'; 
    public const ADMIN_HOME = '/admin/dashboard';


    /**
     * The controller namespace for the application.
     *
     * When present, controller route declarations will automatically be prefixed with this namespace.
     *
     * @var string|null
     */
    // protected $namespace = 'App\\Http\\Controllers';

    /**
     * Define your route model bindings, pattern filters, etc.
     * ルート情報を設定する。bootメソッド→サービスプロバロバイダが読み込まれたあとに実行されるメソッド
     * ルートパターンはミドルウェアのap1とwebを使う2パターン
     * laravelでviewを表示してリクエストレスポンスを返すパターンは→web
     * フロントをすべてjsなどで作る場合はapiを使う
     * @return void
     */
    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            //prefix→URLの頭にapiとつける。Routeはファサード内のメソッドを使ってる
            Route::prefix('api')
            //ミドルウェアをグループのすべてのルートに割り当てる
                ->middleware('api')
                ->namespace($this->namespace)
            //ヘルパ関数base_pathでroutes/api.phpのすべてのURLにapiがつく
                ->group(base_path('routes/api.php'));

            //prefix('/')でownerやadminがついてないURLは/へ遷移させる
            Route::prefix('/')
            //asをつけることで別名にできる'/'→'user.'に変わる
                ->as('user.')
                ->middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));
            
            //owner用
            Route::prefix('owner')
                ->as('owner.')
                ->middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/owner.php'));

            //admin用
            Route::prefix('admin')
                ->as('admin.')
                ->middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/admin.php'));
        });
    }

    /**
     * Configure the rate limiters for the application.
     *
     * @return void
     */
    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
        });
    }
}

4. ガード設定

Laravel標準の認証機能→ user owner adminに認証機能をつける

今回は以下3つを指定

guards・・今回はsession
Providers・・今回はEloquent(モデル)
passwordReset ・・生成したテーブル名 をそれぞれ設定

<?php

return [
'defaults' => [
        'guard' => 'users',
        'passwords' => 'users',
    ],
'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'users' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'owners' => [
            'driver' => 'session',
            'provider' => 'owners',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admin',
        ],
    ],
//それぞれのモデルから情報を取得する
'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        'owners' => [
            'driver' => 'eloquent',
            'model' => App\Models\Owner::class,
        ],

        'admin' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],
/*
|expire 期限(日)、throttle ログイン失敗したら制限(秒)
|provider→1つ上で設定したやつ
|table→マイグレーションファイルで設定した名前
*/

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],

        'owners' => [
            'provider' => 'owners',
            'table' => 'owner_password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],

        'admin' => [
            'provider' => 'admin',
            'table' => 'admin_password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
5. Middreware設定

Middleware/Authenticate.php
ユーザが未認証の場合のリダイレクト処理
たとえばownerのURLにownerでログインしてなかったらownerのログイン画面に飛ばす

Middleware/RedirectIfAuthenticated
ログイン済みユーザーがアクセスしてきたら リダイレクト処理
ownerがログインした状態でowner画面を表示した場合

<?php

namespace App\Http\Middleware;
use Illuminate\Support\Facades\Route;
use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{   //RouteServiceProviderでuser.などは定義済
    protected $user_route = 'user.login';
    protected $owner_route = 'owner.login';
    protected $admin_route = 'admin.login';
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *もし認証されていない場合、リダイレクトする処理を書く
     * @param  \Illuminate\Http\Request  $request
     * @return string|null
     */
    protected function redirectTo($request)
    {   //もしリクエストはjsonじゃなかったら
        if (! $request->expectsJson()) {
            //もしowner関連のURLじゃなかったら$owner_routeへ飛ばす
            if(Route::is('owner.*')){
                //$this->で上のプロパティ呼び出し
                return route($this->owner_route);
                //admin関連のURLでなかったらadminへ飛ばす
            } elseif(Route::id('admin*')){
                return route($this->admin_route);
                //ownerでもadminでもなかったらユーザーへ飛ばす
            } else {
                return route($this->user_route);
            }
        }
    }
}

<?php

namespace App\Http\Middleware;

use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated
{   //config/appで定義
    private const GUARD_USER = 'users';
    private const GUARD_ADMIN = 'admin';
    private const GUARD_OWNER = 'owners';
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
     * @param  string|null  ...$guards
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     */
    public function handle(Request $request, Closure $next, ...$guards)
    {
        // $guards = empty($guards) ? [null] : $guards;
        // //Authのガードでチェックしてログインしてたらリダイレクト
        // foreach ($guards as $guard) {
        //     if (Auth::guard($guard)->check()) {
        //         return redirect(RouteServiceProvider::HOME);
        //     }
        // }

        //もしuserとして認証してるか、またはuser関連のURLなら
        if(Auth::guard(self::GUARD_USER)->check()&& $request->routeIs('user.*')){
            return redirect(RouteServiceProvider::HOME);
        }

        if(Auth::guard(self::GUARD_ADMIN)->check()&& $request->routeIs('admin.*')){
            return redirect(RouteServiceProvider::ADMIN_HOME);
        }

        if(Auth::guard(self::GUARD_OWNER)->check()&& $request->routeIs('owner.*')){
            return redirect(RouteServiceProvider::OWNER_HOME);
        }

        return $next($request);
    }
}

6.リクエストクラス設定

ログインフォームに入力された値をDBの値と比較し、合っていたら認証する
User, Owner, Admin 3つのフォームがあるので routeIs() でルート確認しつつ Auth::guard() を追加

<?php
/**
     * Attempt to authenticate the request's credentials.
     *
     * @return void
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function authenticate()
    {
        $this->ensureIsNotRateLimited();
        //config/auth.phpのgurads設定と紐付ける
        //requestクラスなのでrouteIsがつかえる。ownerのログインフォームからきたらownersとする
        if($this->routeIs('owner*')){
            $guard = 'owners';
        //adminのログインフォームからきたらadminとする
        }elseif($this->routeIs('admin*')){
            $guard = 'admin';
        //それ以外ならusersとする
        }else{
            $guard = 'users';
        }

        //Auth::attemptで入力された情報からPWのemailを受け取る
        if (! Auth::guard($guard)->attempt($this->only('email', 'password'), $this->boolean('remember'))) {
            RateLimiter::hit($this->throttleKey());

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

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