Search code examples
phplaravellaravel-5laravel-routinglaravel-middleware

Intercept Laravel Routing


I am busy building a Restful API in Laravel 5.1 where the API version is passed through the header. This way I can version the features rather than copy and pasting a whole route group and increment the version number.

The problem I'm having is that I would like to have versioned methods, IE:

public function store_v1 (){  }

I have added a middleware on my routes where I capture the version from the header, but now need to modify the request to choose the correct method from the controller.

app/Http/routes.php

Route::group(['middleware' => ['apiversion']], function()
{
    Route::post('demo', 'DemoController@store');
}

app/Http/Middleware/ApiVersionMiddleware.php

public function handle($request, Closure $next)
{
    $action = app()->router->getCurrentRoute()->getActionName();

    //  dd($action)
    //  returns "App\Http\Controllers\DemoController@store"
}

From here on, I would attach the header version to the $action and then pass it through the $request so that it reaches the correct method.

Well, that is the theory anyway.

Any ideas on how I would inject actions into the Route?


Solution

  • I think Middleware might not be the best place to do that. You have access to the route, but it doesn't offer a away to modify the controller method that will be called.

    Easier option is to register a custom route dispatcher that handling the logic of calling controller methods based on the request and the route. It could look like that:

    <?php
    
    class VersionedRouteDispatcher extends Illuminate\Routing\ControllerDispatcher {
      public function dispatch(Route $route, Request $request, $controller, $method)
      {
        $version = $request->headers->get('version', 'v1'); // take version from the header
        $method = sprintf('%s_%s', $method, $version); // create target method name
        return parent::dispatch($route, $request, $controller, $method); // run parent logic with altered method name
      }
    }
    

    Once you have this custom dispatcher, register it in your AppServiceProvider:

    public function register() {
      $this->app->singleton('illuminate.route.dispatcher', VersionedRouteDispatcher::class);
    }
    

    This way you'll overwrite the default route dispatcher with your own one that will suffix controller method names with the version taken from request header.