初めに
Laravel8でマルチログイン、メール認証それぞれの単体記事はたくさんあるのですが、マルチログインを実装した際のメール認証がなく私自身が少し詰まったので、まとめようと思います。初めての記事投稿ですので、お手柔らかに🙇♂️
環境
PHP: 8.0.9
Laravel: 8.52.0
laravel/breeze: 1.4
準備
マルチログインの実装に関する記事はたくさんあるので、マルチログイン実装部分は割愛させていただきます。
ModelはUserとAdminを作成して、prefixとasをつけています。
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
Route::prefix('admin')
->as('admin.')
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/admin.php'));
Route::prefix('/')
->as('user.')
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
});
}
実装
まずは、ドキュメントを確認します。
Laravel8では簡単にメール認証を組み込む方法を用意してくれています。
ドキュメント通りに実装し、いざ新規登録をしてみると、、、
先ほど、RouteServiceProvider.phpでasを指定したため、web.php, auth.phpのルートは先頭に(user.)がつきます。
そのため、verification.verifyが定義されてないよと怒られてしまいます。
また、以下の部分でパスを指定しています。
protected function verificationUrl($notifiable)
{
if (static::$createUrlCallback) {
return call_user_func(static::$createUrlCallback, $notifiable);
}
return URL::temporarySignedRoute(
'verification.verify', ←ここ
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}
vendor配下のコードを直接変更するのはよろしくないので、このクラスをオーバーライドしていきます。
メール通知なのでNotification配下に作成します。vendor/laravel/framework/src/Illuminate/Auth/Notifications/VerifyEmail.phpの中身を作成したVerifyEmailにまるっとコピーしてきて、パスを指定している部分を変更します。
php artisan make:notification User/VerifyEmail
<?php
namespace App\Notifications\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\URL;
class VerifyEmail extends Notification
{
use Queueable;
/**
* The callback that should be used to create the verify email URL.
*
* @var \Closure|null
*/
public static $createUrlCallback;
/**
* The callback that should be used to build the mail message.
*
* @var \Closure|null
*/
public static $toMailCallback;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Build the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$verificationUrl = $this->verificationUrl($notifiable);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
}
return $this->buildMailMessage($verificationUrl);
}
/**
* Get the verify email notification mail message for the given URL.
*
* @param string $url
* @return \Illuminate\Notifications\Messages\MailMessage
*/
protected function buildMailMessage($url)
{
return (new MailMessage)
->subject(Lang::get('Verify Email Address'))
->line(Lang::get('Please click the button below to verify your email address.'))
->action(Lang::get('Verify Email Address'), $url)
->line(Lang::get('If you did not create an account, no further action is required.'));
}
/**
* Get the verification URL for the given notifiable.
*
* @param mixed $notifiable
* @return string
*/
protected function verificationUrl($notifiable)
{
if (static::$createUrlCallback) {
return call_user_func(static::$createUrlCallback, $notifiable);
}
return URL::temporarySignedRoute(
'user.verification.verify',// ←ここ
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}
/**
* Set a callback that should be used when creating the email verification URL.
*
* @param \Closure $callback
* @return void
*/
public static function createUrlUsing($callback)
{
static::$createUrlCallback = $callback;
}
/**
* Set a callback that should be used when building the notification mail message.
*
* @param \Closure $callback
* @return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
そして以下を追加。これは、MustVerifyEmail内のメソッドで新規登録時に送信されるメールの処理です。
public function sendEmailVerificationNotification(){
$this->notify(new VerifyEmail());
}
これは、middlewareのEnsureEmailIsVerifiedで先ほどと同じようにバスを指定している部分でエラーが発生しています。
web.phpなどで指定しているverifiedですね。
...省略
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
Route::get('/dashboard', function () {
return view('user.dashboard');
})->middleware(['auth:users', 'verified'])->name('dashboard');
app\Http\Middleware\EnsureEmailIsVerified.phpを作成し、\Illuminate\Auth\Middleware\EnsureEmailIsVerifiedをまるっとコピーします。namespaceは変更する必要があります。get_classでオブジェクトのクラス名を取得して条件分岐。
<?php
namespace App\Http\Middleware; ←ここ
use Closure;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\URL;
class CustomEnsureEmailIsVerified
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $redirectToRoute
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse|null
*/
public function handle($request, Closure $next, $redirectToRoute = null)
{
if (! $request->user() ||
($request->user() instanceof MustVerifyEmail &&
! $request->user()->hasVerifiedEmail())) {
if(get_class($request->user()) === 'App\Models\User') { ←ここ
$path = 'user.';
} else {
$path = 'admin.';
}
return $request->expectsJson()
? abort(403, 'Your email address is not verified.')
: Redirect::guest(URL::route($redirectToRoute ?: $path. 'verification.notice'));
}
return $next($request);
}
}
あとは送られてきたメールからアクセスし、ログインできれば完了です!!
もう一つのモデルも同じように作成すればOKです。
参考
もし間違っている点や、もっと良い書き方などあればぜひ教えてください🙇♂️