九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、Laravel
開発をしたことがある人なら誰でも聞いたことがあるパッケージが「Laravel Breeze」じゃないでしょうか。
これは以下のような機能を一手に引き受けてくれるスグレモノで、私もよくお世話になっています。
- ログイン
- ユーザー登録
- パスワード再発行
- 本登録用のURLをメール送信
などなど。
しかし、実はこのLaravel Breeze
はuser
という名前のログインタイプだけでなく、拡張すれば別のものも利用できるのをご存知でしょうか。
例えば・・・・・・
- student
- teacher
- parent
※ この機能はMulti Auth
(まるち・おーす)と呼ばれる機能です。
そして、Multi Auth
を利用することでDB
テーブルを分けることができ、各ユーザーの登録内容が違う場合などにとても便利です。
しかし❗
実は、ログインだけならそれほど難しくはないのですが、全ての機能をMulti Auth
化するとなかなかやっかいだったりします。
そこで❗
今回は今後の自分のための備忘録としてもLaravel Breeze
でMulti Auth
を使う方法を記事としてまとめました。
ぜひ何かのお役に立てば嬉しいです。😄✨
※ ちなみに今回の記事はホントにたくさんの箇所で作成&変更が必要で私自身コンガラがってしまいました。。もしどこか抜けている部分があったらぜひ教えてください。🙏✨
「ちょっと記事が長くなるかな…
が想定の3倍ぐらいになってしまいました(笑)」
開発環境: Laravel 10.x(今回はフロントエンドは使用せず、Blade
を使ってます)
目次
やりたいこと
今回は学生さんたちがログインする「student」というログインタイプ(guard
)を作ってみます。
つまり、モデルはStudent.php
になりますし、URLも/student
で始まります。
また、今回は「student」だけですが、もし「teacher」や「seller」などその他のタイプを追加する場合は同じ作業をすればOKです。
ただし、今回はログイン後の部分は省略しています。
あまりにも長い記事になりすぎたので…💦
※ なお、少し悩みましたがコントローラーやモデルの共通化はしないことにしました。というのも、ログインするユーザータイプによって例外が発生することが想定されるからです。(もし共通化したい場合はBase******Controller
などをつくり、それを継承してそれぞれのコントローラーをつくるといいでしょう)
では、楽しんでやっていきましょう❗
Laravel Breeze をインストールする
まずはログイン機能がLaravel
で使えないと始まりませんので、Laravel Breeze
をインストールします。
以下のコマンドを実行してください。
composer require laravel/breeze --dev
すると、パッケージがインストールされるので、続けて以下のコマンドを実行します。
php artisan breeze:install blade php artisan migrate
※ ちなみに、今回はVue
などのフロントエンドは使わずBlade
のみで実装しますが、基本的には同じです。(ビューとかは変更してください)
これで、DB
にテーブルが作成されログイン機能がインストールされました。
ヘルパー関数をつくる
では、ここからがMulti Auth
の作業にはいりますが、まずはどこからでも「今どのログインタイプかが分かる」ようにヘルパー関数を作っておきます。
この関数は現在のURL
からキーワードを切り出し、例えば「student」などを返します。
app/helpers.php
<?php use Illuminate\Support\Str; if (! function_exists('multi_auth_guard')) { function multi_auth_guard(): ?string { $current_uri = request()->path(); $guard_types = ['student']; // 👈 複数ある場合はここに追加する foreach ($guard_types as $guard_type) { if(Str::startsWith($current_uri, $guard_type)) { return $guard_type; } } return null; } }
※ なお、この中にある$guard_types
に含まれているものがMulti Auth
の種類(guard
)になります。もしstudent
の他にも増やしたい場合は以下のように変更してください。
$guard_types = ['student', 'teacher', 'parent'];
Laravel
へ登録します。composer.json
"autoload": { "files": [ "app/helpers.php" ], "psr-4": { "App\\": "app/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/" } },
※ files
の部分を追加しました。
では、登録できたら以下のコマンドで実際に有効にします。
composer dump-autoload
これで、helpers.php
がどこにあるかがLaravel
に分かるようになり、登録されたヘルパー関数がどこからでも実行できるようになります。
View composerをつくる
View composer
とは、シンプルに言うと「先に値をセットしておけば、どのビューからでも取り出せるよ👍」機能のことです。
つまり、いちいち以下のようにしなくてもよくなるというスグレモノです。
return view('home', [ 'test' => 'テスト文字列', // 👈 毎回セットするのがメンドウ! ]);
そして、セットする値は先ほどヘルパー関数の中身(ログインタイプ)です。
では、次のファイルを作成してください。
app/Providers/ViewServiceProvider.php
<?php namespace App\Providers; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; class ViewServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { View::composer('*', function ($view) { $multi_auth_guard = multi_auth_guard(); if(! is_null($multi_auth_guard)) { $view->with('multi_auth_guard', $multi_auth_guard); } }); } }
そして、このファイルをLaravel
側へ登録しておきましょう。
config/app.php
// 省略 /* * Package Service Providers... */ App\Providers\ViewServiceProvider::class, // 👈 ここを追加しました // 省略
これで、どのビューからでも$multi_auth_guard
にアクセスすることができるようになりました。
guard(ログインタイプ)を設定ファイルに書き込む
次に、これも準備のようなものですがLaravel
にログインタイプ(guard
)をセットします。
今回はstudent
だけですので、以下のようになります。
config/auth.php
// 省略 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'student' => [ // 👈 ここを追加しました 'driver' => 'session', 'provider' => 'students', ], ], // 省略 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], 'students' => [ // 👈 ここを追加しました 'driver' => 'eloquent', 'model' => App\Models\Student::class, ], ], // 省略 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_reset_tokens', 'expire' => 60, 'throttle' => 60, ], 'students' => [ // 👈 ここを追加しました 'provider' => 'students', 'table' => 'password_reset_tokens', 'expire' => 60, 'throttle' => 60, ], ],
※ ⚠ご注意: ちなみに、書き方は自由ですがLaravel
のお作法に沿って書いていますので、「student」と「students」の単数形/複数形が入り混じっています。お気をつけください❗
これで、「student」というログインタイプが追加されました。
モデル・マイグレーション・Factory・Seeder をつくる
では、次にDB周り(モデル・マイグレーション・Factory・Seeder)の作業をしていきます。
以下のコマンドを実行してください。
php artisan make:model Student -msf
すると、4つのファイルが作成されるので、それぞれ中身を変更します。
モデル
app/Models/Student.php
<?php namespace App\Models; use App\Notifications\ResetPasswordForMultiAuth; // 👈 追加しました use App\Notifications\VerifyEmailForMultiAuth;// 👈 追加しました use Illuminate\Contracts\Auth\MustVerifyEmail; // 👈 コメントアウトを解除しました use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; class Student extends Authenticatable implements MustVerifyEmail // MustVerifyEmailを追加しました { use HasApiTokens, HasFactory, Notifiable; /** * 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', 'password' => 'hashed', ]; // 👇 以降、オーバーライドしました public function sendPasswordResetNotification($token) { $this->notify(new ResetPasswordForMultiAuth($token)); } public function sendEmailVerificationNotification() { $this->notify(new VerifyEmailForMultiAuth()); } }
マイグレーション
database/migrations/****_**_**_******_create_students_table.php
// 省略 Schema::create('students', 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(); }); // 省略
Factory
database/factories/StudentFactory.php
// 省略 public function definition(): array { return [ 'name' => fake()->name(), 'email' => fake()->unique()->safeEmail(), 'email_verified_at' => now(), 'password' => Hash::make('password'), 'remember_token' => Str::random(10), ]; } // 省略
Seeder
database/seeders/StudentSeeder.php
<?php namespace Database\Seeders; use Database\Factories\StudentFactory; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class StudentSeeder extends Seeder { /** * Run the database seeds. */ public function run(): void { for($i = 0; $i < 10; $i++) { $email = 'student' . $i . '@example.com'; StudentFactory::new()->create([ 'email' => $email, ]); } } }
ちなみに、私の場合すぐ定義場所に移動できなくなるので、
User::factory()->create();
という書き方はしません。(もしかして、こっちのほうが主流なのかな…🤔)
Seeder
は作っただけでは有効になりませんので、DatabaseSeeder
へ登録します。
<?php namespace Database\Seeders; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. */ public function run(): void { // 省略 $this->call([ // 👈 ここを追加しました StudentSeeder::class, ]); } }
では、この状態でデータベースを初期化してみましょう。
以下のコマンドを実行してください。
php artisan migrate:fresh --seed
すると、実際のテーブルはこうなりました。
Notification をつくる
先ほど、モデルの中で2つのNotification
(≒ メール送信)をセットしましたのでこれらをつくります。
※ ちなみに、初期状態を踏まえてNotification
を使っていますが、個人的にはあまり使い勝手がいいと感じないので、Mailable
を使うほうが好みです。HTMLメールに対応していない場合もまれにあるようですしね😉
パスワード再発行の時のメール送信
以下のコマンドを実行してください。
php artisan make:notification ResetPasswordForMultiAuth
そして、作成されたファイルの中身を次のように変更します。
app/Notifications/ResetPasswordForMultiAuth.php
<?php namespace App\Notifications; use Illuminate\Auth\Notifications\ResetPassword; class ResetPasswordForMultiAuth extends ResetPassword { protected function resetUrl($notifiable) { if (static::$createUrlCallback) { return call_user_func(static::$createUrlCallback, $notifiable, $this->token); } $multi_auth_guard = multi_auth_guard(); $route_name = $multi_auth_guard . '.password.reset'; return url(route($route_name, [ 'token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset(), ], false)); } }
※ なお、このNotification
は初期状態で利用されるIlluminate\Auth\Notifications\ResetPassword
を拡張したものです。
Email verification の時のメール送信
次にEmail verification
(本登録用のURLを送信する機能)のためのNotification
です。
以下のコマンドを実行してください。
php artisan make:notification VerifyEmailForMultiAuth
ファイルが作成されるので、中身を次のようにします。
<?php namespace App\Notifications; use Illuminate\Auth\Notifications\VerifyEmail as BaseVerifyEmail; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\URL; class VerifyEmailForMultiAuth extends BaseVerifyEmail { private $multi_auth_guard; public function __construct() { $this->multi_auth_guard = multi_auth_guard(); } protected function verificationUrl($notifiable) { if (static::$createUrlCallback) { return call_user_func(static::$createUrlCallback, $notifiable); } $route_name = $this->multi_auth_guard .'.verification.verify'; return URL::temporarySignedRoute( $route_name, Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)), [ 'id' => $notifiable->getKey(), 'hash' => sha1($notifiable->getEmailForVerification()), ] ); } }
※ これも、元々のIlluminate\Auth\Notifications\VerifyEmail
を拡張したものです。
コントローラーを拡張する
では、続いて各種コントローラーを作っていきます。
今回はstudent
というguard
ですので、app/Http/Controllers/Student
フォルダへこれらのコントローラーを設置します。
※ なお、できるだけ汎用的につくっているので、ネームスペースを変更するだけでOKだと思います。
ログイン
app/Http/Controllers/Student/AuthenticatedSessionController.php
<?php namespace App\Http\Controllers\Student; use App\Http\Controllers\Controller; use App\Http\Requests\Auth\MultiAuthLoginRequest; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\View\View; class AuthenticatedSessionController extends Controller { private $multi_auth_guard; public function __construct() { $this->multi_auth_guard = multi_auth_guard(); } /** * Display the login view. */ public function create(): View { return view($this->multi_auth_guard .'.auth.login'); } /** * Handle an incoming authentication request. */ public function store(MultiAuthLoginRequest $request): RedirectResponse { $request->authenticate(); $request->session()->regenerate(); $redirect_url = route($this->multi_auth_guard .'.dashboard'); // ログイン後のリダイレクト先 return redirect()->intended($redirect_url); } /** * Destroy an authenticated session. */ public function destroy(Request $request): RedirectResponse { Auth::guard($this->multi_auth_guard)->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); return to_route($this->multi_auth_guard .'.login'); } }
ユーザー登録
app/Http/Controllers/Student/RegisteredUserController.php
<?php namespace App\Http\Controllers\Student; use App\Http\Controllers\Controller; use Illuminate\Auth\Events\Registered; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\Rules; use Illuminate\View\View; class RegisteredUserController extends Controller { private $multi_auth_guard; public function __construct() { $this->multi_auth_guard = multi_auth_guard(); } /** * Display the registration view. */ public function create(): View { return view($this->multi_auth_guard .'.auth.register'); } /** * Handle an incoming registration request. * * @throws \Illuminate\Validation\ValidationException */ public function store(Request $request): RedirectResponse { $model_name = 'App\\Models\\'. ucfirst($this->multi_auth_guard); $request->validate([ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:'. $model_name], 'password' => ['required', 'confirmed', Rules\Password::defaults()], ]); $user = $model_name::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($request->password), ]); event(new Registered($user)); auth($this->multi_auth_guard)->login($user); return to_route($this->multi_auth_guard .'.dashboard'); } }
パスワード再発行
app/Http/Controllers/Student/PasswordResetLinkController.php
<?php namespace App\Http\Controllers\Student; use App\Http\Controllers\Controller; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Password; use Illuminate\Support\Str; use Illuminate\View\View; class PasswordResetLinkController extends Controller { private $multi_auth_guard; public function __construct() { $this->multi_auth_guard = multi_auth_guard(); } /** * Display the password reset link request view. */ public function create(): View { return view($this->multi_auth_guard .'.auth.forgot-password'); } /** * Handle an incoming password reset link request. * * @throws \Illuminate\Validation\ValidationException */ public function store(Request $request): RedirectResponse { $request->validate([ 'email' => ['required', 'email'], ]); $provider_name = Str::plural($this->multi_auth_guard); // 複数形にする // We will send the password reset link to this user. Once we have attempted // to send the link, we will examine the response then see the message we // need to show to the user. Finally, we'll send out a proper response. $status = Password::broker($provider_name)->sendResetLink( $request->only('email') ); return $status == Password::RESET_LINK_SENT ? back()->with('status', __($status)) : back()->withInput($request->only('email')) ->withErrors(['email' => __($status)]); } }
app/Http/Controllers/Student/NewPasswordController.php
<?php namespace App\Http\Controllers\Student; use App\Http\Controllers\Controller; use Illuminate\Auth\Events\PasswordReset; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Password; use Illuminate\Support\Str; use Illuminate\Validation\Rules; use Illuminate\View\View; class NewPasswordController extends Controller { private $multi_auth_guard; public function __construct() { $this->multi_auth_guard = multi_auth_guard(); } /** * Display the password reset view. */ public function create(Request $request): View { return view($this->multi_auth_guard .'.auth.reset-password', ['request' => $request]); } /** * Handle an incoming new password request. * * @throws \Illuminate\Validation\ValidationException */ public function store(Request $request): RedirectResponse { $request->validate([ 'token' => ['required'], 'email' => ['required', 'email'], 'password' => ['required', 'confirmed', Rules\Password::defaults()], ]); $provider_name = Str::plural($this->multi_auth_guard); // Here we will attempt to reset the user's password. If it is successful we // will update the password on an actual user model and persist it to the // database. Otherwise we will parse the error and return the response. $status = Password::broker($provider_name)->reset( $request->only('email', 'password', 'password_confirmation', 'token'), function ($user) use ($request) { $user->forceFill([ 'password' => Hash::make($request->password), 'remember_token' => Str::random(60), ])->save(); event(new PasswordReset($user)); } ); $route_name = $this->multi_auth_guard .'.login'; // If the password was successfully reset, we will redirect the user back to // the application's home authenticated view. If there is an error we can // redirect them back to where they came from with their error message. return $status == Password::PASSWORD_RESET ? redirect()->route($route_name)->with('status', __($status)) : back()->withInput($request->only('email')) ->withErrors(['email' => __($status)]); } }
Email verification
「Email verification」とは、ユーザー登録するときに一旦メールアドレスに本登録URLを送信し、そのリンクがクリックされたら実際に登録になるという機能です(つまり、ちゃんとメールアドレスが存在しているか確認しているわけですね)
app/Http/Controllers/Student/EmailVerificationNotificationController.php
<?php namespace App\Http\Controllers\Student; use App\Http\Controllers\Controller; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class EmailVerificationNotificationController extends Controller { /** * Send a new email verification notification. */ public function store(Request $request): RedirectResponse { $multi_auth_guard = multi_auth_guard(); $intended_url = route($multi_auth_guard .'.dashboard'); if ($request->user()->hasVerifiedEmail()) { return redirect()->intended($intended_url); } $request->user()->sendEmailVerificationNotification(); return back()->with('status', 'verification-link-sent'); } }
app/Http/Controllers/Student/EmailVerificationPromptController.php
<?php namespace App\Http\Controllers\Student; use App\Http\Controllers\Controller; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; class EmailVerificationPromptController extends Controller { /** * Display the email verification prompt. */ public function __invoke(Request $request): RedirectResponse|View { $multi_auth_guard = multi_auth_guard(); $intended_url = route($multi_auth_guard .'.dashboard'); $view_name = $multi_auth_guard .'.auth.verify-email'; return $request->user()->hasVerifiedEmail() ? redirect()->intended($intended_url) : view($view_name); } }
app/Http/Controllers/Student/VerifyEmailController.php
<?php namespace App\Http\Controllers\Student; use App\Http\Controllers\Controller; use Illuminate\Auth\Events\Verified; use Illuminate\Foundation\Auth\EmailVerificationRequest; use Illuminate\Http\RedirectResponse; class VerifyEmailController extends Controller { /** * Mark the authenticated user's email address as verified. */ public function __invoke(EmailVerificationRequest $request): RedirectResponse { $multi_auth_guard = multi_auth_guard(); $redirect_route_name = $multi_auth_guard .'.dashboard'; if ($request->user()->hasVerifiedEmail()) { return to_route($redirect_route_name); } if ($request->user()->markEmailAsVerified()) { event(new Verified($request->user())); } return to_route($redirect_route_name); } }
FormRequest をつくる
次に、AuthenticatedSessionController.php
で使ったMultiAuthLoginRequest
をつくります。
以下のコマンドを実行してください。
php artisan make:request Auth\\MultiAuthLoginRequest
すると、ファイルが作成されるので中身を次のように変更します。
app/Http/Requests/Auth/MultiAuthLoginRequest.php
<?php namespace App\Http\Requests\Auth; use Illuminate\Auth\Events\Lockout; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; class MultiAuthLoginRequest extends FormRequest { /** * Determine if the user is authorized to make this request. */ public function authorize(): bool { return true; } /** * Get the validation rules that apply to the request. * * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string> */ public function rules(): array { return [ 'email' => ['required', 'string', 'email'], 'password' => ['required', 'string'], ]; } /** * Attempt to authenticate the request's credentials. * * @throws \Illuminate\Validation\ValidationException */ public function authenticate(): void { $this->ensureIsNotRateLimited(); $multi_auth_guard = multi_auth_guard(); if (! auth($multi_auth_guard)->attempt($this->only('email', 'password'), $this->boolean('remember'))) { RateLimiter::hit($this->throttleKey()); throw ValidationException::withMessages([ 'email' => trans('auth.failed'), ]); } RateLimiter::clear($this->throttleKey()); } /** * Ensure the login request is not rate limited. * * @throws \Illuminate\Validation\ValidationException */ public function ensureIsNotRateLimited(): void { if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { return; } event(new Lockout($this)); $seconds = RateLimiter::availableIn($this->throttleKey()); throw ValidationException::withMessages([ 'email' => trans('auth.throttle', [ 'seconds' => $seconds, 'minutes' => ceil($seconds / 60), ]), ]); } /** * Get the rate limiting throttle key for the request. */ public function throttleKey(): string { return Str::transliterate(Str::lower($this->input('email')).'|'.$this->ip()); } }
各種ミドルウェアを作成&変更する
Multi Auth
を実現するためにはいくつかミドルウェアが必要になってきます。
それぞれ作成&変更してください。
ログイン失敗時のリダイレクト先を変更する
このミドルウェアはすでに存在していますので、中身を以下のように変更します。
app/Http/Middleware/Authenticate.php
<?php namespace App\Http\Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware; use Illuminate\Http\Request; class Authenticate extends Middleware { /** * Get the path the user should be redirected to when they are not authenticated. */ protected function redirectTo(Request $request): ?string { if (! $request->expectsJson()) { $multi_auth_guard = multi_auth_guard(); if(! is_null($multi_auth_guard)) { return route($multi_auth_guard .'.login'); } return url('/'); } return null; } }
Email Verification が完了しているかチェックするミドルウェア
このミドルウェアは存在していませんので、以下のコマンドで新規作成します。
php artisan make:middleware EnsureEmailIsVerifiedForMultiAuth
すると、ファイルが作成されるので中身を次のようにします。
app/Http/Middleware/EnsureEmailIsVerifiedForMultiAuth.php
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Support\Facades\Redirect; use Illuminate\Support\Facades\URL; use Symfony\Component\HttpFoundation\Response; class EnsureEmailIsVerifiedForMultiAuth { /** * Handle an incoming request. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle($request, Closure $next, $redirectToRoute = null): Response { $multi_auth_guard = multi_auth_guard(); $redirect_route_name = $multi_auth_guard .'.verification.notice'; if (! $request->user() || ($request->user() instanceof MustVerifyEmail && ! $request->user()->hasVerifiedEmail())) { return $request->expectsJson() ? abort(403, 'Your email address is not verified.') : Redirect::guest(URL::route($redirectToRoute ?: $redirect_route_name)); } return $next($request); } }
そして、このミドルウェアをLaravel
に登録してください。
app/Http/Kernel.php
<?php namespace App\Http; use App\Http\Middleware\EnsureEmailIsVerifiedForMultiAuth; // 👈 ここを追加しました use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { // 省略 protected $middlewareAliases = [ // 省略 'verified_for_multi_auth' => EnsureEmailIsVerifiedForMultiAuth::class, // 👈 ここを追加しました ]; }
すでにログイン済みのときのリダイレクト先を変更する
app/Http/Middleware/RedirectIfAuthenticated.php
<?php namespace App\Http\Middleware; use App\Providers\RouteServiceProvider; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; class RedirectIfAuthenticated { /** * Handle an incoming request. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next, string ...$guards): Response { $guards = empty($guards) ? [null] : $guards; foreach ($guards as $guard) { if (Auth::guard($guard)->check()) { $multi_auth_guard = multi_auth_guard(); if($guard === $multi_auth_guard) { return to_route($multi_auth_guard .'.dashboard'); } return redirect(RouteServiceProvider::HOME); } } return $next($request); } }
※ 太字の部分を追加しています
ビューをつくる
ブラウザで表示されることになるHTML
を含んだコードをつくっていきます。
ログイン
resources/views/student/auth/login.blade.php
<x-guest-layout> <!-- Session Status --> <x-auth-session-status class="mb-4" :status="session('status')" /> <form method="POST" action="{{ route($multi_auth_guard .'.login') }}"> @csrf <!-- Email Address --> <div> <x-input-label for="email" :value="__('Email')" /> <x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus autocomplete="username" /> <x-input-error :messages="$errors->get('email')" class="mt-2" /> </div> <!-- Password --> <div class="mt-4"> <x-input-label for="password" :value="__('Password')" /> <x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="current-password" /> <x-input-error :messages="$errors->get('password')" class="mt-2" /> </div> <!-- Remember Me --> <div class="block mt-4"> <label for="remember_me" class="inline-flex items-center"> <input id="remember_me" type="checkbox" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500" name="remember"> <span class="ml-2 text-sm text-gray-600">{{ __('Remember me') }}</span> </label> </div> <div class="flex items-center justify-end mt-4"> @if (Route::has('password.request')) <a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route($multi_auth_guard .'.password.request') }}"> {{ __('Forgot your password?') }} </a> @endif <x-primary-button class="ml-3"> {{ __('Log in') }} </x-primary-button> </div> </form> </x-guest-layout>
ユーザー登録
resources/views/student/auth/register.blade.php
<x-guest-layout> <form method="POST" action="{{ route($multi_auth_guard .'.register') }}"> @csrf <!-- Name --> <div> <x-input-label for="name" :value="__('Name')" /> <x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" /> <x-input-error :messages="$errors->get('name')" class="mt-2" /> </div> <!-- Email Address --> <div class="mt-4"> <x-input-label for="email" :value="__('Email')" /> <x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autocomplete="username" /> <x-input-error :messages="$errors->get('email')" class="mt-2" /> </div> <!-- Password --> <div class="mt-4"> <x-input-label for="password" :value="__('Password')" /> <x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" /> <x-input-error :messages="$errors->get('password')" class="mt-2" /> </div> <!-- Confirm Password --> <div class="mt-4"> <x-input-label for="password_confirmation" :value="__('Confirm Password')" /> <x-text-input id="password_confirmation" class="block mt-1 w-full" type="password" name="password_confirmation" required autocomplete="new-password" /> <x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" /> </div> <div class="flex items-center justify-end mt-4"> <a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route($multi_auth_guard .'.login') }}"> {{ __('Already registered?') }} </a> <x-primary-button class="ml-4"> {{ __('Register') }} </x-primary-button> </div> </form> </x-guest-layout>
パスワード再発行
resources/views/student/auth/forgot-password.blade.php
<x-guest-layout> <form method="POST" action="{{ route($multi_auth_guard .'.register') }}"> @csrf <!-- Name --> <div> <x-input-label for="name" :value="__('Name')" /> <x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" /> <x-input-error :messages="$errors->get('name')" class="mt-2" /> </div> <!-- Email Address --> <div class="mt-4"> <x-input-label for="email" :value="__('Email')" /> <x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autocomplete="username" /> <x-input-error :messages="$errors->get('email')" class="mt-2" /> </div> <!-- Password --> <div class="mt-4"> <x-input-label for="password" :value="__('Password')" /> <x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" /> <x-input-error :messages="$errors->get('password')" class="mt-2" /> </div> <!-- Confirm Password --> <div class="mt-4"> <x-input-label for="password_confirmation" :value="__('Confirm Password')" /> <x-text-input id="password_confirmation" class="block mt-1 w-full" type="password" name="password_confirmation" required autocomplete="new-password" /> <x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" /> </div> <div class="flex items-center justify-end mt-4"> <a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route($multi_auth_guard .'.login') }}"> {{ __('Already registered?') }} </a> <x-primary-button class="ml-4"> {{ __('Register') }} </x-primary-button> </div> </form> </x-guest-layout>
resources/views/student/auth/reset-password.blade.php
<x-guest-layout> <form method="POST" action="{{ route($multi_auth_guard .'.password.store') }}"> @csrf <!-- Password Reset Token --> <input type="hidden" name="token" value="{{ $request->route('token') }}"> <!-- Email Address --> <div> <x-input-label for="email" :value="__('Email')" /> <x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email', $request->email)" required autofocus autocomplete="username" /> <x-input-error :messages="$errors->get('email')" class="mt-2" /> </div> <!-- Password --> <div class="mt-4"> <x-input-label for="password" :value="__('Password')" /> <x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" /> <x-input-error :messages="$errors->get('password')" class="mt-2" /> </div> <!-- Confirm Password --> <div class="mt-4"> <x-input-label for="password_confirmation" :value="__('Confirm Password')" /> <x-text-input id="password_confirmation" class="block mt-1 w-full" type="password" name="password_confirmation" required autocomplete="new-password" /> <x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" /> </div> <div class="flex items-center justify-end mt-4"> <x-primary-button> {{ __('Reset Password') }} </x-primary-button> </div> </form> </x-guest-layout>
Email verification
resources/views/student/auth/verify-email.blade.php
<x-guest-layout> <div class="mb-4 text-sm text-gray-600"> {{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }} </div> @if (session('status') == 'verification-link-sent') <div class="mb-4 font-medium text-sm text-green-600"> {{ __('A new verification link has been sent to the email address you provided during registration.') }} </div> @endif <div class="mt-4 flex items-center justify-between"> <form method="POST" action="{{ route($multi_auth_guard .'.verification.send') }}"> @csrf <div> <x-primary-button> {{ __('Resend Verification Email') }} </x-primary-button> </div> </form> <form method="POST" action="{{ route($multi_auth_guard .'.logout') }}"> @csrf <button type="submit" class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"> {{ __('Log Out') }} </button> </form> </div> </x-guest-layout>
Dashboard(ログイン後のページ)
resources/views/student/dashboard.blade.php
{{ $multi_auth_guard }} でログイン中です。 <form method="post" action="./logout"> @csrf <button type="submit">ログアウト</button> </form>
ルートをつくる
Laravel Breeze
をインストールすると、何もしなくてもroutes/auth.php
というファイルが作成されますので、これをコピーし、routes/student.php
というファイルをつくり、中身を次のように変更します。
routes/student.php
<?php use App\Http\Controllers\Student\AuthenticatedSessionController; use App\Http\Controllers\Student\ConfirmablePasswordController; use App\Http\Controllers\Student\EmailVerificationNotificationController; use App\Http\Controllers\Student\EmailVerificationPromptController; use App\Http\Controllers\Student\NewPasswordController; use App\Http\Controllers\Student\PasswordController; use App\Http\Controllers\Student\PasswordResetLinkController; use App\Http\Controllers\Student\RegisteredUserController; use App\Http\Controllers\Student\VerifyEmailController; use Illuminate\Support\Facades\Route; Route::prefix('student')->name('student.')->group(function () { Route::middleware('guest:student')->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.store'); }); Route::middleware('auth:student')->group(function () { Route::get('verify-email', EmailVerificationPromptController::class) ->name('verification.notice'); Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) ->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::put('password', [PasswordController::class, 'update'])->name('password.update'); Route::post('logout', [AuthenticatedSessionController::class, 'destroy']) ->name('logout'); Route::middleware('verified_for_multi_auth')->group(function () { Route::get('dashboard', function () { return view('student.dashboard'); })->name('dashboard'); }); }); });
routes/web.php
// 省略 require __DIR__.'/auth.php'; require __DIR__.'/student.php'; // 👈 ここを追加しました
なお、もし初期状態のuserを使ったログイン等が不要の場合は、
require __DIR__.'/auth.php';
の部分をコメントアウトや削除してもいいでしょう。
テストしてみる
今回の実行結果はLaravel Breeze
と同じなのでテストは省略します(文章長くてちょっと疲れましたしね😂)
企業様へのご提案
Laravel
の構成を根本から利用するすると、Multi Auth
だけでなくいろいろな改造を施すことができます。
もしそういった機能をご希望でしたらいつでもお気軽にお問い合わせからご相談ください。
お待ちしております。😄✨
おわりに
ということで今回はLaravel Breeze
の機能をフルに活かしながらMulti Auth
を実装してみました。
とはいえ、今回は取り扱わなかったですが、以下のような機能もあるみたいですので、また機会があったらやってみたいと思います。
- パスワードを入力しないとアクセスできないページをつくる
- パスワードの変更
Laravel Breeze
って機能多すぎですね(笑)
作者さんには感謝しています😄✨
また、終わってみてから思いついたのですが、Multi Auth
に対応したstub
を作っておけば楽かもですね。
みなさんも、ぜひいろいろと改造してみてくださいね。
ではでは〜❗
「鬼滅の刃みたいに
真ん中で割れた石、
カッコよかったです👍」