Laravelのパスワードリセットを日本語化したい

# はじめに
LaravelのPasswordResetをAuthを使って作成したのですが、
このままでは英語のメールとなってしまうので、
日本語化したいと思います。

Auth周りの動きなどあまり把握できていなかった部分もついでにざっと流れを追っておこうと思います。
ちなみにLaravelは初心者です。

LaravelのVersion

```bash

$ 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がありこれがルーティングさせています。

```php:vendor/laravel/framework/src/Illuminate/Routing/Router.php

    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/```のことですのでここから探しましょう。
見てみるといました。

```php:SendsPasswordResetEmails.php

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については以下のようになっています。

```php:email.blade.php

<form class="password_reset_form" method="POST" action="{{ route('password.email') }}">
```

### 入力フォームをPOSTしてから
先ほどの入力フォームから、```password/email```に対してPOSTされることができましたので
再度ルーティングを確認すると、

```php:vendor/laravel/framework/src/Illuminate/Routing/Router.php

    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行目くらいにいました。


```php:SendsPasswordResetEmails.php
    /**
     * 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変数を定義している箇所からです。

```php

        $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の呼び出し
です。

```php:Illuminate/Auth/Passwords/PasswordBroker.php

    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: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: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：Illuminate/Foundation/Auth/ResetsPasswords.php

<?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メソッドを確認してみます。


```php:Illuminate/Foundation/Auth/ResetsPasswords.php

    /**
     * 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);
    }
```

とても丁寧にコメントが（英語で）書いてありますね。
２つ塊がありますが、
上の方でパスワードをリセットをし、下の方でリダイレクト処理を行なっています。
リセットの処理は基本的には触らなくていいと思うので、
リダイレクトのみ変更いたします。
sendResetResponse()をみて見ます。

```php

    /**
     * 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でオーバーライドします。

```php:app/user.php

    // オーバーライド
    // パスワードリセットのメールタイトルと送信者を日本語に変更する
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new JaPasswordReset($token));
    }
}
```

また、通知でメール送信するため、
日本語用の通知クラスも作成します。

```bash

php artisan make:notification JaPasswordReset
```
そうするとapp/Notifications配下に指定したクラスが作成されます。
あとはその中にタイトルや送信元を設定してあげればOKです。
コンストラクタ内でtokenを定義しないとメール内で使用できないので忘れずに変更しましょう。

```php:app/Notifications/JaPasswordReset.php
<?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：App/Http/Controllers/Auth/ResetPasswordController.php

<?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)の動きを調べてみる](https://qiita.com/zaburo/items/9fcf0f4c771e011a4d35)
[Laravel5.8でパスワードリセット処理（メール送信まで）を追ってみた](https://qiita.com/ntm718/items/c28c5e62b3b5a6f44a3e)
[Laravel 5.7 通知](https://readouble.com/laravel/5.7/ja/notifications.html)
[laravel 5.3 パスワードリセットメールをカスタマイズする](https://qiita.com/honeytrap15/items/09a6f68a077fad34658e)