【Laravel】return back() で何が起きてるか調べてみた

#はじめに
バリデーションに失敗した場合など直前のページにリダイレクトする場合があるが、
そのときに使用する`return back()`について何が起こっているかを調べてみた。

```php:example.php
Route::post('user/profile', function () {
    // リクエストのバリデート処理…

    return back(); // これが何をしているのか調べたい
});
```

#バージョン
PHP 7.4.20
Laravel 6.18.43

#前提知識
`return back()`のback()の正体はグローバルに定義されたヘルパ関数である。

>無効なフォームが送信された場合など、まれにユーザーを直前のページへリダイレクトする必要が起きます。グローバルなbackヘルパ関数を使用することで行なえます。この機能はセッションを利用しているため、back関数を呼び出すルートがwebミドルウェアグループを使用しているか、全セッションミドルウェアを確実に適用してください。

参考：[Laravel 6.x HTTPリダイレクト](https://readouble.com/laravel/6.x/ja/redirects)

#backヘルパの中身を見る

backヘルパ関数は以下にあった。
\laravel\vendor\laravel\framework\src\Illuminate\Foundation\helpers.php

```php:helpers.php
if (! function_exists('back')) {
    /**
     * Create a new redirect response to the previous location.
     *
     * @param  int  $status
     * @param  array  $headers
     * @param  mixed  $fallback
     * @return \Illuminate\Http\RedirectResponse
     */
    function back($status = 302, $headers = [], $fallback = false)
    {
        return app('redirect')->back($status, $headers, $fallback);
    }
}
```
独自にbackというメソッドを定義していなければ、backメソッドを呼んだ時にbackヘルパが呼ばれる。
`return back()`では`app('redirect')->back(302, [], false)`を返している。

#app('redirect')->back(302, [], false)が何をしてるのか見る

app()自体もback同様にヘルパ関数であり、インスタンスを生成する。

>app関数は、サービスコンテナのインスタンスを返します。
>コンテナにより依存解決する、クラス名かインターフェイス名を渡すこともできます。

参考：[Laravel 6.x ヘルパ](https://readouble.com/laravel/6.x/ja/helpers.html#method-app)

クラス名かインターフェイス名を渡すということだが、ここでは'redirect'という文字列を渡している。
\Illuminate\Foundation\Application.phpを見ると、'redirect'というエイリアスで[\Illuminate\Routing\Redirector::class]が登録されている。

```php:Application.php
    public function registerCoreContainerAliases()
    {
        foreach ([
           // 他もいっぱいあるけど、多いので'redirect'以外は省略
           // ～～～
            'redirect'             => [\Illuminate\Routing\Redirector::class],
           // ～～～
           // 他もいっぱいあるけど、多いので'redirect'以外は省略
        ] as $key => $aliases) {
            foreach ($aliases as $alias) {
                $this->alias($key, $alias);
            }
        }
    }
```

したがって`app('redirect')->back(302, [], false)`は
**Redirectorクラスのインスタンスを生成し、そのインスタンスのbackメソッドを(302, [], false)という引数で呼び出しているということになる。**

#Redirectorクラスのback(302, [], false)が何をしてるのか見る

backメソッドでは同クラス内にあるcreateRedirectメソッドを呼んでいる。

```php:Redirector.php
    public function back($status = 302, $headers = [], $fallback = false)
    {
        return $this->createRedirect($this->generator->previous($fallback), $status, $headers);
    }
```

#createRedirect($this->generator->previous($fallback), $status, $headers)が何をしてるのか見る

createRedirectメソッドの中ではtap関数にRedirectResoponseクラスのインスタンスとクロージャを渡している。

```php:Redirector.php
    protected function createRedirect($path, $status, $headers)
    {
        return tap(new RedirectResponse($path, $status, $headers), function ($redirect) {
            if (isset($this->session)) {
                $redirect->setSession($this->session);
            }

            $redirect->setRequest($this->generator->getRequest());
        });
    }
```
tap関数はこれまたヘルパであり、第二引数のクロージャに第一引数を渡す（使うと見た目がすっきりする...らしい）。

第一引数はRedirectResponseクラスのインスタンスであり、コンストラクタとしてback関数から受け取った引数を渡す。

| 引数 | 値 |
|:----------:|:-----------:|
| $path       | generatorで作成された遷移直前のページのURI        |
| $status     | HTTPレスポンスコード302（リダイレクト）      |
| $headers       | HTTPレスポンスに設定するヘッダ        |


第二引数のクロージャではRedirectResponseクラスのインスタンスを受け取る。
Redirectorインスタンスにsessionが設定済の場合はRedirectorインスタンスが保持するsession変数をsessionに格納する。
その後setRequestメソッドで、生成したRedirectResponseインスタンスの$requestにgeneratorクラスのgetRequestメソッドで取得したrequestをセットする。

tap関数の戻り値は第一引数で受け取ったインスタンスになるので、
createRedirectで作成したRedirectResponseインスタンスが最終的なreturn back();の戻り値となり、リダイレクトが行われる。

#結論
return back()の戻り値は、直前のページのURI・レスポンスコード302を保持したRedirectResponseクラスのインスタンスである。