Laravel SubstituteBindingsミドルウェア

Laravel SubstituteBindingsミドルウェアについて調べてみた。
SubstituteBindingsミドルウェアは、Kernel.phpでwebとapiグループのミドルウェアとして指定されている。
Substitute = 代替(代わりに使う)。Binding = 紐づける

```Kernel.php
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];
```

## SubstituteBindingsは
ルートモデルバインディング用の機能。
https://laravel.com/docs/6.x/routing#route-model-binding

routeにfunctionを入れてモデルを引数にすると、pathの値を元に自動でfindOrFailして引数として渡してくれる。
{user} pathでUserが取得できない場合は404エラーが返される。

```api.php
Route::get('/user/{user}', function (\App\Models\User $user) {
    return $user;
});
```

```bash
curl http://127.0.0.1:8000/api/user/1
{"id":1,"name":"aaa","email":"hoge@co.jp","email_verified_at":null,"api_token":"WzLJB6Djg7HS9vDp8Ss8RqcIKYPYvs2pJBUl8CYMwsnVCHXK3FeIEdLBFq8Q","created_at":"2020-12-04T14:46:11.000000Z","updated_at":"2020-12-04T14:46:11.000000Z"}%     
```

SubstituteBindingsをミドルウェアから外した状態で再度アクセスするとLaravelコンテナによってUserモデルがインスタンス化された状態で引数に追加される。($userはnullではないが$user->idとかはnullになる)

## SubstituteBindings読む
SubstituteBindingsのコンストラクタでRegisterを引数でもらっている。

```SubstituteBindings.php
    public function __construct(Registrar $router)
    {
        $this->router = $router;
    }
```

handle関数では、bindingを行っている。
binding処理はコンストラクタで受け取ったRegisterクラスで行っている。

```
    public function handle($request, Closure $next)
    {
        $this->router->substituteBindings($route = $request->route());

        $this->router->substituteImplicitBindings($route);

        return $next($request);
    }
```


## Register
Illuminate\Foundation\Application.phpのregisterCoreContainerAliases関数でRegisterのaliasが設定されている。
Registerクラスは\Illuminate\Routing\Router::classの型を使ってDIされる。


```Application.php
'router'               => [\Illuminate\Routing\Router::class, \Illuminate\Contracts\Routing\Registrar::class, \Illuminate\Contracts\Routing\BindingRegistrar::class],
```

## substituteBindings
substituteBindingsは明示的なpathとModelの結合の処理に使用される。
substituteBindingsの関数を見ると、routeのparameterを元にパラメータを設定している。
Routerにはpath名と型を紐づけるbinderを管理するbidnersプロパティがあり、そこに該当するbinderが設定されているとperformBinding関数を使ってパラメータを取得し、setParameterでセットしている。
binderはbind関数で追加されるが、これはmodel関数で使用されている。
model関数は `Route::model('user', \App\Models\User::class);` のように使われる。

```Router.php
    public function substituteBindings($route)
    {
        foreach ($route->parameters() as $key => $value) {
            if (isset($this->binders[$key])) {
                $route->setParameter($key, $this->performBinding($key, $value, $route));
            }
        }

        return $route;
    }
```

↓routeのパラメータはpathを分解した時のpath名をkeyに値をvalueにした配列になっている。

```
Route::get('hoge/{hogeId}', [\App\Http\Controllers\HogeController::class, 'hoge']);
↓route->parameters()の値
{"hogeId":"11"}
```


## substituteImplicitBindings
substituteImplicitBindingsは暗黙的なpathとモデルの結合に使用される。
Implicit = 暗黙的