LaravelのRoute::resourceを追っかける

Laravelを初めて使って、
まず便利だなーと思ったのが`Route::resource()`

これを基本使うようにすれば、変なメソッドを生やす意識も減る。

また、既存のControllerに似てるけど
チョット外れたことをしようとした時に、
「いやいや、新しいController切ろう」と気づかせてくれる。

ただ１つメソッドが煩雑化することを気づかせてくれるというのは
すごいな----と思いました


なので今回は感動を与えてくれる`Route::resource()`の中身を追ってみる


# 何が出来るの
役割はルーティング
　
> リソースに対する様々なアクションを処理する、複数のルートがこの１定義により生成されます。

と本家( https://readouble.com/laravel/5.3/ja/controllers.html#resource-controllers )にあるように
Routeの記述を楽にさせてくれる

# どこにあるの

## 実体を探す
`config/app.php`をみると
aliasに記述がされている

```php:config/app.php
    'aliases' => [
        .....
        'Route' => Illuminate\Support\Facades\Route::class,
        .....
    ],
```

Facadeにaliasが張ってあり
中身を見ると、実体は `Illuminate/Routing/Router.php`にあるよーとなってる
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Support/Facades/Route.php

```php:Illuminate/Support/Facades/Route.php
    protected static function getFacadeAccessor()
    {
        return 'router';
    }
```

## resource()の中身を見る
ずっとIlluminate/Routing/Router.phpを読んでいくと
resourceの記述を発見

引数は、Resourceを設定する時に与える値の
ルーティングする名前とそれに紐づくコントローラー名

```php:Illuminate/Routing/Router.php
    /**
     * Route a resource to a controller.
     *
     * @param  string  $name
     * @param  string  $controller
     * @param  array  $options
     * @return void
     */
    public function resource($name, $controller, array $options = [])
    {
        if ($this->container && $this->container->bound('Illuminate\Routing\ResourceRegistrar')) {
            $registrar = $this->container->make('Illuminate\Routing\ResourceRegistrar');
        } else {
            $registrar = new ResourceRegistrar($this);
        }
        $registrar->register($name, $controller, $options);
    }
```

中では`Illuminate\Routing\ResourceRegistrar`をnewしている

### ResourceRegistrar

読んでみると
Resourceのコアというか、処理自体が分かるのは
このResourceRegistrarぽい
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Routing/ResourceRegistrar.php

まず`resource()`で呼ばれた`register()`を見てみる

```php:
    /**
     * Route a resource to a controller.
     *
     * @param  string  $name
     * @param  string  $controller
     * @param  array   $options
     * @return void
     */
    public function register($name, $controller, array $options = [])
    {
        if (isset($options['parameters']) && ! isset($this->parameters)) {
            $this->parameters = $options['parameters'];
        }
        // If the resource name contains a slash, we will assume the developer wishes to
        // register these resource routes with a prefix so we will set that up out of
        // the box so they don't have to mess with it. Otherwise, we will continue.
        if (Str::contains($name, '/')) {
            $this->prefixedResource($name, $controller, $options);
            return;
        }
        // We need to extract the base resource from the resource name. Nested resources
        // are supported in the framework, but we need to know what name to use for a
        // place-holder on the route parameters, which should be the base resources.
        $base = $this->getResourceWildcard(last(explode('.', $name)));
        $defaults = $this->resourceDefaults;
        foreach ($this->getResourceMethods($defaults, $options) as $m) {
            $this->{'addResource'.ucfirst($m)}($name, $base, $controller, $options);
        }
    }
```

めっちゃコメント書いてある....

部分的に読み解いていく

#### ルーティング名に`/`が入っていた時の処理

ここの部分

```php
        if (Str::contains($name, '/')) {
            $this->prefixedResource($name, $controller, $options);
            return;
        }
```
 
同じclass内にある`prefixedResource()`を読んで要約すると

ルーティング名に`/`が入っていたら
スラッシュの前の部分を`Route:group()`で定義するよ！
(ルーティングのグループ化: https://readouble.com/laravel/5.3/ja/routing.html#route-groups)
という処理のようだ

groupを設定したあとは、再帰処理する形でまた`resource()`引数を渡していく


#### 各リソースをrouteに登録していく

この部分

```
        $base = $this->getResourceWildcard(last(explode('.', $name)));
        $defaults = $this->resourceDefaults;
        foreach ($this->getResourceMethods($defaults, $options) as $m) {
            $this->{'addResource'.ucfirst($m)}($name, $base, $controller, $options);
        }
```

`explode('.', $name)`の部分の意味がわからない....

`$this->resourceDefaults`には
resourceで登録されるControllerのメソッド集が入ってる(ex:index,show,create...)
そのメソッドごとにrouteの登録処理が行われる

例としてIndexのルートをみてみる

```php
    /**
     * Add the index method for a resourceful route.
     *
     * @param  string  $name
     * @param  string  $base
     * @param  string  $controller
     * @param  array   $options
     * @return \Illuminate\Routing\Route
     */
    protected function addResourceIndex($name, $base, $controller, $options)
    {
        $uri = $this->getResourceUri($name);
        $action = $this->getResourceAction($name, $controller, 'index', $options);
        return $this->router->get($uri, $action);
    }
```

ルーティングしたい名前を定義して
resourceのルールに従った挙動を習得する
routerにURIと挙動を渡したら登録完了！


これを全メソッド分(それぞれで処理が少し異なるが基本一緒)を行い
ルーティングが登録されていく


# 余談

読んでいる中で見つけた`resources()`

resourceも配列で渡せば複数resourceが登録できるみたい！
知らなかった！コレは使いたい！


```php:Illuminate/Routing/Router.php
    /**
     * Register an array of resource controllers.
     *
     * @param  array  $resources
     * @return void
     */
    public function resources(array $resources)
    {
        foreach ($resources as $name => $controller) {
            $this->resource($name, $controller);
        }
    }
```