2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravel のルート定義をメソッドチェインでいい感じに書けるようにするやつ

Posted at

前に Laravel のルート定義でコントローラーのクラス名とメソッド名を別々に書きたい みたいなのを書いた後、それっぽくルート定義を書けるようにするためのクラスを作りました。

マジックメソッドでミドルウェアがシュッと書けるので、ミドルウェアの記述がスッキリします。

<?php
namespace App\Routing;

use Illuminate\Routing\Router;

/**
 * @method $this auth()
 * @method $this can(string $arg)
 */
class AppRouteCollector
{
    /**
     * @var Router
     */
    private $router;

    /**
     * @var array
     */
    private $methods = [];

    /**
     * @var array
     */
    private $paths = [];

    /**
     * @var array
     */
    private $actions = [];

    /**
     * @var array
     */
    private $names = [];

    /**
     * @var array
     */
    private $middlewares = [];

    /**
     * @var array
     */
    private $wheres = [];

    /**
     * @var bool
     */
    private $last = true;

    public function __construct(Router $router)
    {
        $this->router = $router;
    }

    public function __destruct()
    {
        if (!$this->last) {
            return;
        }

        $this->router->match(
            self::generateMethods($this->methods),
            self::generatePath($this->paths),
            [
                'uses' => self::generateUses($this->actions),
                'as' => self::generateAs($this->names),
                'middleware' => self::generateMiddleware($this->middlewares),
                'where' => self::generateWhere($this->wheres),
            ]
        );
    }

    private static function generateMethods(array $methods): array
    {
        $methods = array_map(function (string $method) {
            return strtoupper($method);
        }, $methods);
        return $methods;
    }

    private static function generatePath(array $paths): string
    {
        $paths = array_map(function (string $path) {
            return trim($path, '/');
        }, $paths);
        $paths = array_filter($paths, function (string $path) {
            return strlen($path) > 0;
        });
        return '/' . implode('/', $paths);
    }

    private static function generateUses(array $actions): string
    {
        $actions = array_map(function (string $action) {
            return trim($action, '\\');
        }, $actions);

        if ($actions && end($actions)[0] === '@') {
            $method = array_pop($actions);
            $actions = implode('\\', $actions) . $method;
        } else {
            $actions = implode('\\', $actions);
        }

        return $actions;
    }

    private static function generateAs(array $names): string
    {
        return implode('', $names);
    }

    private static function generateMiddleware(array $middlewares): array
    {
        $arr = [];
        foreach ($middlewares as $middleware => $arg) {
            if (is_bool($arg)) {
                $arg = var_export($arg, true);
            }
            if ($arg !== null) {
                $middleware = "$middleware:$arg";
            }
            $arr[] = $middleware;
        }
        return $arr;
    }

    private static function generateWhere(array $wheres): array
    {
        return $wheres;
    }

    /**
     * @return static
     */
    private function chain()
    {
        $this->last = false;
        $new = clone $this;
        $new->last = true;
        return $new;
    }

    /**
     * @param string $method
     * @param string $path
     *
     * @return static
     */
    public function method(string $method, string $path)
    {
        $new = $this->path($path);
        $new->methods[] = strtoupper($method);
        return $new;
    }

    /**
     * @param string $path
     *
     * @return static
     */
    public function get(string $path)
    {
        return $this->method(__FUNCTION__, $path);
    }

    /**
     * @param string $path
     *
     * @return static
     */
    public function post(string $path)
    {
        return $this->method(__FUNCTION__, $path);
    }

    /**
     * @param string $path
     *
     * @return static
     */
    public function put(string $path)
    {
        return $this->method(__FUNCTION__, $path);
    }

    /**
     * @param string $path
     *
     * @return static
     */
    public function patch(string $path)
    {
        return $this->method(__FUNCTION__, $path);
    }

    /**
     * @param string $path
     *
     * @return static
     */
    public function delete(string $path)
    {
        return $this->method(__FUNCTION__, $path);
    }

    /**
     * @param string $path
     *
     * @return static
     */
    public function options(string $path)
    {
        return $this->method(__FUNCTION__, $path);
    }

    /**
     * @param string $path
     *
     * @return static
     */
    public function path($path)
    {
        $new = $this->chain();
        $new->paths[] = trim($path, '/');
        return $new;
    }

    /**
     * @param string $action
     *
     * @return static
     */
    public function action(string $action)
    {
        $new = $this->chain();
        $new->actions[] = $action;
        return $new;
    }

    /**
     * @param string $name
     *
     * @return static
     */
    public function name(string $name)
    {
        $new = $this->chain();
        $new->names[] = $name;
        return $new;
    }

    /**
     * @param string $middleware
     * @param mixed  $arg
     *
     * @return static
     */
    public function middleware(string $middleware, $arg = null)
    {
        $new = $this->chain();
        $new->middlewares[$middleware] = $arg;
        return $new;
    }

    /**
     * @param string $where
     * @param string $expr
     *
     * @return static
     */
    public function where(string $where, string $expr)
    {
        $new = $this->chain();
        $new->wheres[$where] = $expr;
        return $new;
    }

    /**
     * @param string $name
     * @param array  $arguments
     *
     * @return static
     */
    public function __call($name, $arguments)
    {
        return $this->middleware($name, $arguments[0] ?? null);
    }

    /**
     * @param string $name
     *
     * @return static
     */
    public function __get($name)
    {
        return $this->middleware($name);
    }

    /**
     * @param callable $callback
     */
    public function group(callable $callback): void
    {
        $new = $this->chain();
        $new->last = false;
        $callback($new);
    }
}

次のように使います。middleware('can:admin')can('admin') のように書けます。静的解析にも優しいです。

use App\Routing\AppRouteCollector as R;

$r = resolve(R::class);

$r->auth()->group(function (R $r) {
    $r->action(UserController::class)->can('admin')->group(function (R $r) {
        $r->get('/users')->action('@index')->name('user.list');
        $r->get('/users/create')->action('@create')->name('user.create');
        $r->post('/users')->action('@store')->name('user.store');
        $r->get('/users/{user}')->action('@show')->name('user.show');
        $r->get('/users/{user}/edit')->action('@edit')->name('user.edit');
        $r->put('/users/{user}')->action('@update')->name('user.update');
        $r->delete('/users/{user}')->action('@destroy')->name('user.destroy');
    });
});

さいごに

ついカッとなって作ったものの、我に返って冷静になると別に素のままの Laravel のルート定義でも良いか・・・と思ったのでお蔵入り。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?