基本的な機能だけ用意されたFortify
実利用となると結構カスタマイズしなきゃいけない気がしている
今回はパスワードリセットのviewとメールまわりを修正したくなった時に
いじった場所を上げていきます
基本的にメソッド上書きして対応です
カスタマイズ対象
- viewの変更
- 任意のメールの送信
- Notificationsを使う
パスワードリセットのプロセスを変更したい場合は
App\Actions\ResetUserPassword を変更するといいよと公式では言ってます
https://readouble.com/laravel/9.x/ja/fortify.html#resetting-the-password
しかし、メールとviewの差し替えだけなんだよなぁと思い
今回は対象箇所だけミニマムに対応していこうかと思います
viewのカスタマイズ
// パスワード忘れた人への画面
// メールアドレスを入力させる
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メソッドを追記
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クラスに渡してあげる
<?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()で呼ばれてます
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
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
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