LoginSignup
16
11

More than 5 years have passed since last update.

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

Posted at

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

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

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

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

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

何が出来るの

役割はルーティング
 

リソースに対する様々なアクションを処理する、複数のルートがこの1定義により生成されます。

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

どこにあるの

実体を探す

config/app.phpをみると
aliasに記述がされている

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

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

resource()の中身を見る

ずっとIlluminate/Routing/Router.phpを読んでいくと
resourceの記述を発見

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

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()を見てみる

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

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

部分的に読み解いていく

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

ここの部分

        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のルートをみてみる

    /**
     * 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が登録できるみたい!
知らなかった!コレは使いたい!

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);
        }
    }
16
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
11