LoginSignup
0
0

More than 1 year has passed since last update.

Fortify利用におけるパスワードリセットのviewとメールのカスタマイズ

Posted at

基本的な機能だけ用意されたFortify
実利用となると結構カスタマイズしなきゃいけない気がしている

今回はパスワードリセットのviewとメールまわりを修正したくなった時に
いじった場所を上げていきます
基本的にメソッド上書きして対応です

カスタマイズ対象

  • viewの変更
  • 任意のメールの送信
    • Notificationsを使う

パスワードリセットのプロセスを変更したい場合は
App\Actions\ResetUserPassword を変更するといいよと公式では言ってます
https://readouble.com/laravel/9.x/ja/fortify.html#resetting-the-password

しかし、メールとviewの差し替えだけなんだよなぁと思い
今回は対象箇所だけミニマムに対応していこうかと思います

viewのカスタマイズ

app/Providers/FortifyServiceProvider.php
        
        // パスワード忘れた人への画面
        // メールアドレスを入力させる
        Fortify::requestPasswordResetLinkView(function () {
            return view('auth.forget-password'); // 直すのここ
            // view先でPOSTを{{ route('password.email') }}にしておく
        });

        // 新規パスワード入力画面
        Fortify::resetPasswordView(function ($request) {
            return view('auth.reset-password', ['request' => $request]); // 直すのここ
            // view先でPOSTを{{ route('password.update') }}にしておく
        });

送信メールのカスタマイズ

送るメールクラスを変更したいのでカスタマイズしていきます

sendPasswordResetNotificationメソッドを追記

app/Models/User.php
use App\Notifications\PasswordResetNotification;

class User extends Authenticatable
    /**
     * パスワードリセット通知の送信を上書き
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token): void
    {
        // パスワードリセットリクエストがあった時に行いたい処理をここに書く
        // 今回はNotificationを差し替え
        $this->notify(new PasswordResetNotification($token)); 
    }

PasswordResetNotificationを作る

resetUrl()でリセットリンクを発行してMailクラスに渡してあげる

app/Notifications/PasswordResetNotification.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;

use App\Mail\PasswordResetMail; // ご希望のメールクラスを

class PasswordResetNotification extends Notification
{
    use Queueable;

    public $token;
    protected $subject = '【madree】パスワードリセットのお知らせ';

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($token)
    {
      $this->token = $token;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        $to = $notifiable->email;
        $url = $this->resetUrl($notifiable);

        $options = [
            'from' => config('mail.from.address'),
            'from_jp' => config('mail.from.name'),
            'to' => $to,
            'subject' => $this->subject,
            'template' => 'mail.passwordreset',
        ];
        $data = [
            'url' => $url,
        ];
        // ご希望のメールクラスを指定
        $mail = new PasswordResetMail($options, $data);

        return $mail->to($to);
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }

    /**
     * Get the reset URL for the given notifiable.
     * src/Illuminate/Auth/Notifications/ResetPassword.php から拝借
     *
     * @param  mixed  $notifiable
     * @return string
     */
    protected function resetUrl(mixed $notifiable): string
    {
        return url(route('password.reset', [
            'token' => $this->token,
            'email' => $notifiable->getEmailForPasswordReset(),
        ], false));
    }
}

参考: Fortify元の仕組みを見ておく

メール送信呼び出し元

メール忘れた人への画面からのPOST先
Fortifyの中にあるPasswordResetLinkController内のstoreにてsendResetLink()で呼ばれてます

vendor/laravel/fortify/src/Http/Controllers/PasswordResetLinkController.php
class PasswordResetLinkController extends Controller
{
    // ......
    public function store(Request $request): Responsable
    {
        // ....
        $status = $this->broker()->sendResetLink(
            $request->only(Fortify::email())
        );

そのあとはAuthのPasswordBrokerのなかのPasswordBroker()が呼ばれる
callbackは今回ないのでuserに生えたsendPasswordResetNotification()が次に実行される
https://github.com/laravel/framework/blob/9.x/src/Illuminate/Auth/Passwords/PasswordBroker.php#L48

src/Illuminate/Auth/Passwords/PasswordBroker.php#L48
class PasswordBroker implements PasswordBrokerContract
{
    // ...
    public function sendResetLink(array $credentials, Closure $callback = null)
    {
        // ...
        if ($callback) {
            $callback($user, $token);
        } else {
            // Once we have the reset token, we are ready to send the message out to this
            // user with a link to reset their password. We will then redirect back to
            // the current URI having nothing set in the session to indicate errors.
            $user->sendPasswordResetNotification($token);
        }

sendPasswordResetNotificationはUserで読み込まれている
use Illuminate\Foundation\Auth\User as Authenticatable;
に定義されている

以下のように掘り進めると
vendor/laravel/framework/src/Illuminate/Foundation/Auth/User.php

vendor/laravel/framework/src/Illuminate/Auth/Passwords/CanResetPassword.php

src/Illuminate/Auth/Passwords/CanResetPassword.php#L25
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;

trait CanResetPassword
{
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPasswordNotification($token));
    }

ResetPasswordNotificationは
よくあるNotificationで
メール(vendor/laravel/framework/src/Illuminate/Notifications/Messages/MailMessage.php)を送っている

最後に

パスワードリセットにおけるviewとメール周りのカスタマイズを書いてきました。
Userモデル内のsendPasswordResetNotification()で
わざわざNotification使わずに、力強くMailクラスでsendしてもよかったのかなと思ったりもしましたが、
上書きしているコードの流儀に沿った方がいいかなという気持ちもあり、
そのままNotificationを新規で作るというやりかたにしました。
Fortifyは自由度高くて困っちゃうことが多々あるんですが、適宜カスタムをやっていくしかないかなと思ってます。


弊社では、一緒に働いてくれるエンジニアを募集しています。

↓興味のある方はこちらから↓
Wantedly - スタジオアンビルト株式会社

こんなWebサービスを作っています。
マドリー
Studio Unbuilt

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0