はじめに
LaravelのPasswordResetをAuthを使って作成したのですが、
このままでは英語のメールとなってしまうので、
日本語化したいと思います。
Auth周りの動きなどあまり把握できていなかった部分もついでにざっと流れを追っておこうと思います。
ちなみにLaravelは初心者です。
LaravelのVersion
$ php artisan --version
Laravel Framework 5.5.48
処理の流れを追っていこう
/password/resetで入力フォームを表示するまで
まず、パスワード リセット画面は/password/reset
となります。
routes/web.php
内に記載があるAuth::routes();
でルーティングされてます。
そのAuth::routes();
ですが、これによって
vendor/laravel/framework/src/Illuminate/Routing/Router.php
が呼ばれます。
上記の中にauth fuctionがありこれがルーティングさせています。
public function auth()
{
// Authentication Routes...
$this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
$this->post('login', 'Auth\LoginController@login');
$this->post('logout', 'Auth\LoginController@logout')->name('logout');
// Registration Routes...
$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
$this->post('register', 'Auth\RegisterController@register');
// Password Reset Routes...
$this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
$this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
$this->post('password/reset', 'Auth\ResetPasswordController@reset');
}
$this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
とあるので、これを見にいきましょう。
app/Http/Controllers/Auth
に移動してもらうと、
ForgotPasswordController.php
がありますがこの中には処理はありません。
実際はこいつがuseしているuse Illuminate\Foundation\Auth\SendsPasswordResetEmails;
の中にあるようなのでさらに見にいきます。
ちなみにちなみにIlluminateではじまるpathが結構ありますが、
vendor/laravel/framework/src/Illuminate/
のことですのでここから探しましょう。
見てみるといました。
namespace Illuminate\Foundation\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
trait SendsPasswordResetEmails
{
/**
* Display the form to request a password reset link.
*
* @return \Illuminate\Http\Response
*/
public function showLinkRequestForm()
{
return view('auth.passwords.email');
}
よってViewはresources/views/auth/passwords
配下のemail.blade.php
ということがわかります。
この画面にてメールアドレスなどの入力画面と送信ボタンが実装されています。
よってパスワードリセット周りを修正する場合はここをいじれば良さそうです。
また、formについては以下のようになっています。
<form class="password_reset_form" method="POST" action="{{ route('password.email') }}">
入力フォームをPOSTしてから
先ほどの入力フォームから、password/email
に対してPOSTされることができましたので
再度ルーティングを確認すると、
public function auth()
{
// Authentication Routes...
$this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
$this->post('login', 'Auth\LoginController@login');
$this->post('logout', 'Auth\LoginController@logout')->name('logout');
// Registration Routes...
$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
$this->post('register', 'Auth\RegisterController@register');
// Password Reset Routes...
$this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
$this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
$this->post('password/reset', 'Auth\ResetPasswordController@reset');
}
すると今度もForgotPasswordController.php
へルーティングされていますが、
どうせこの中には処理はありません。
実際はこいつがuseしているuse Illuminate\Foundation\Auth\SendsPasswordResetEmails;
を見にいくと20行目くらいにいました。
/**
* Send a reset link to the given user.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);
// 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.
$response = $this->broker()->sendResetLink(
$request->only('email')
);
return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($response)
: $this->sendResetLinkFailedResponse($request, $response);
}
順に追っていきましょう。
($this->validateEmail($request);
はバリデーションなので飛ばします。)
まず、response変数を定義している箇所からです。
$response = $this->broker()->sendResetLink(
$request->only('email')
);
同クラスSendsPasswordResetEmails.php
内にあるbrokerメソッドを呼び出していますが、
これがreturn Password::broker();
しています。
ファサードですので、Illuminate/Support/Facades/password.phpを見に行くと、
コメントで@see \Illuminate\Auth\Passwords\PasswordBroker
とあるのでこちらを確認しにいきます。
実際に見てみるとsendResetLinkが定義されているのでこれが実行されていますね。
この中でやっていることとしては
・パラメーターからのユーザーの検索
・sendPasswordResetNotificationの呼び出し
です。
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
if (is_null($user)) {
return static::INVALID_USER;
}
// 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(
$this->tokens->create($user)
);
return static::RESET_LINK_SENT;
}
バリデーションの後ですが、$this->tokens->create($user)
でトークンを作成、引数としてsendPasswordResetNotificationを呼び出してます。
なお、$userには、app/userのインスタンスがgetUserによって返却されています。
sendPasswordResetNotificationですが、
Illuminate/Foundation/Auth/User内でCanResetPasswordをuseし使用されています。
Illuminate/Foundation/Auth/Passwords/CanResetPassword.phpでは以下のように実装されています。
<?php
namespace Illuminate\Auth\Passwords;
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
trait CanResetPassword
{
/**
* Get the e-mail address where password reset links are sent.
*
* @return string
*/
public function getEmailForPasswordReset()
{
return $this->email;
}
/**
* Send the password reset notification.
*
* @param string $token
* @return void
*/
public function sendPasswordResetNotification($token)
{
$this->notify(new ResetPasswordNotification($token));
}
}
notifyメソッドを使ってNotifiableトレイトを呼び出してます。
Illuminate/Auth/Notifications/ResetPassword.php
は以下の通りとなっており、こちらでメール送付していることがわかります。
<?php
namespace Illuminate\Auth\Notifications;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
class ResetPassword extends Notification
{
/**
* The password reset token.
*
* @var string
*/
public $token;
/**
* Create a notification instance.
*
* @param string $token
* @return void
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* Get the notification's channels.
*
* @param mixed $notifiable
* @return array|string
*/
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)
{
return (new MailMessage)
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', url(config('app.url').route('password.reset', $this->token, false)))
->line('If you did not request a password reset, no further action is required.');
}
}
メールを送信してから再登録フォームが表示されるまで
ここまで来るとAuth周りの処理はだいぶ追えるようになってきます。
リセット画面で送信ボタンを押すと、
vendor/laravel/framework/src/Illuminate/Routing/Router.php
の処理で
Auth\ResetPasswordController@showResetForm
に飛ばされます。
app/Http/Controllers/Auth/ResetPasswordController
には特に処理が記述されていないので、
use Illuminate\Foundation\Auth\ResetsPasswords;
とある場所に見に行くと以下の記載があります。
<?php
namespace Illuminate\Foundation\Auth;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Auth\Events\PasswordReset;
trait ResetsPasswords
{
use RedirectsUsers;
/**
* Display the password reset view for the given token.
*
* If no token is present, display the link request form.
*
* @param \Illuminate\Http\Request $request
* @param string|null $token
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function showResetForm(Request $request, $token = null)
{
return view('auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);
}
ここでviewを返却しているようです。
登録フォームでボタン押下後
登録ボタンを押すと、
パスワード変更、リダイレクトの処理へと進んでいきます。
Viewをみるとボタンの箇所にaction="{{ route('password.request') }}"
と記載があります。
冒頭のルーティングを確認すると
$this->post('password/reset', 'Auth\ResetPasswordController@reset');
とありますが、ResetPasswordController
のresetメソッドを確認してみます。
/**
* Reset the given user's password.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
public function reset(Request $request)
{
$this->validate($request, $this->rules(), $this->validationErrorMessages());
// 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.
$response = $this->broker()->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
}
);
// 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 $response == Password::PASSWORD_RESET
? $this->sendResetResponse($response)
: $this->sendResetFailedResponse($request, $response);
}
とても丁寧にコメントが(英語で)書いてありますね。
2つ塊がありますが、
上の方でパスワードをリセットをし、下の方でリダイレクト処理を行なっています。
リセットの処理は基本的には触らなくていいと思うので、
リダイレクトのみ変更いたします。
sendResetResponse()をみて見ます。
/**
* Get the response for a successful password reset.
*
* @param string $response
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetResponse($response)
{
return redirect($this->redirectPath())
->with('status', trans($response));
}
メールを日本語化するには
上記に記載下通り、
メール送付までの過程でUserモデルのsendPasswordResetNotificationを呼び出していることがわかりました。
現在はこれがIlluminate/Foundation/Auth/Passwords/CanResetPassword.phpの方を見てしまっているので、
これをオーバーライドしましょう。
ちなみに、Vendor配下は/gitignorで無視されてしまうので、
app/user.phpでオーバーライドします。
// オーバーライド
// パスワードリセットのメールタイトルと送信者を日本語に変更する
public function sendPasswordResetNotification($token)
{
$this->notify(new JaPasswordReset($token));
}
}
また、通知でメール送信するため、
日本語用の通知クラスも作成します。
php artisan make:notification JaPasswordReset
そうするとapp/Notifications配下に指定したクラスが作成されます。
あとはその中にタイトルや送信元を設定してあげればOKです。
コンストラクタ内でtokenを定義しないとメール内で使用できないので忘れずに変更しましょう。
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class JaPasswordReset extends Notification
{
use Queueable;
/**
* 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)
{
return (new MailMessage)
->subject(__('パスワードリセットの準備ができました'))
->from(config('mail.from.address'))
->view('vendor.notifications.email')
->action(__('パスワードリセット'), url('password/reset', $this->token));
// ->line('The introduction to the notification.')
// ->action('Notification Action', url('/'))
// ->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
リセット後の画面をカスタマイズするには
流れは同じでIlluminateで見ている方をオーバーライドしてやります。
今回はIlluminate/Foundation/Auth/ResetsPasswords
をオーバーライドしたいので、
app/Http/Controllers/Auth/ResetPasswordController
を更新します。
showResetForm
を追加します。
use Illuminate\Http\Request;
を入れないと怒られるので注意です。
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = '/login';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
public function showResetForm(Request $request, $token = null)
{
return view('auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);
}
}
これでapp/resources/views/passwards/
配下のViewが返されるようになります。
終わり
思ったより時間がかかったが、
一通り処理が終えたので良かったとします。
参考
Laravelの標準Authentication(Auth)の動きを調べてみる
Laravel5.8でパスワードリセット処理(メール送信まで)を追ってみた
Laravel 5.7 通知
laravel 5.3 パスワードリセットメールをカスタマイズする