Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 5.3 SubstituteBindings middleware with withoutMiddleware issue

Since Laravel 5.3, the route implicit binding works as middleware called SubstituteBindings. I used to work with Laravel 5.2 and upgraded to 5.3.

I have some custom middlewares in my application and in my tests I need to disable them. So, until now I used $this->withoutMiddleware() in the test methods. But since the update to Laravel 5.3, withoutMiddleware stops the route implicit binding, and all my tests fails.

I don't know if this should be considered as bug, but it is a huge problem for me. Is there any way to set the SubstituteBindings middleware as mandatory middleware? How can I still use implicit binding and test my tests without other middlewares?

like image 288
nrofis Avatar asked Aug 30 '16 19:08

nrofis


1 Answers

Building on my comment above I had a look at registering a custom router which always adds SubstituteBindings to the list of middleware if middleware was disabled. You can achieve it by registering a custom RoutingServiceProvider and registering your own Router class. Unfortunately since the route is created fairly early on in the app bootstrap process you also need to create a custom App class and use that in bootstrap/app.php too.

RoutingServiceProvider

<?php namespace App\Extensions\Providers;

use Illuminate\Routing\RoutingServiceProvider as IlluminateRoutingServiceProvider;
use App\Extensions\ExtendedRouter;

class RoutingServiceProvider extends IlluminateRoutingServiceProvider
{
    protected function registerRouter()
    {
        $this->app['router'] = $this->app->share(function ($app) {
            return new ExtendedRouter($app['events'], $app);
        });
    }
}

Custom router

This adds the middleware, it just extends the default router but overrides the runRouteWithinStack method and, instead of returning an empty array if $this->container->make('middleware.disable') is true, it returns an array containing the SubstituteBindings class.

<?php namespace App\Extensions;

use Illuminate\Routing\Router;
use Illuminate\Routing\Route;
use Illuminate\Routing\Pipeline;
use Illuminate\Http\Request;

class ExtendedRouter extends Router {

    protected function runRouteWithinStack(Route $route, Request $request)
    {
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                                $this->container->make('middleware.disable') === true;

        // Make sure SubstituteBindings is always used as middleware
        $middleware = $shouldSkipMiddleware ? [
            \Illuminate\Routing\Middleware\SubstituteBindings::class
        ] : $this->gatherRouteMiddleware($route);

        return (new Pipeline($this->container))
                    ->send($request)
                    ->through($middleware)
                    ->then(function ($request) use ($route) {
                        return $this->prepareResponse(
                            $request, $route->run($request)
                        );
                    });
    }
}

Custom App Class

<?php namespace App;

use App\Extensions\Providers\RoutingServiceProvider;

class MyCustomApp extends Application
{
    protected function registerBaseServiceProviders()
    {
        parent::registerBaseServiceProviders();
        $this->register(new RoutingServiceProvider($this));
    }

Using the custom app class

In bootstrap/app.php change the line where the app is instantiated to:

$app = new App\MyCustomApp(
    realpath(__DIR__.'/../')
);

--

Warning! I haven't fully tested this, my app loads and my tests pass but there could be issues that I haven't discovered. It's also quite brittle since if the Laravel base Router class changes you might find things break randomly on future upgrades.

--

You might also want to refactor this so the list of middleware in the custom router always contains the SubstituteBindings class so there isn't so much of a difference in behaviour if middleware is disabled.

like image 111
andyberry88 Avatar answered Oct 29 '22 04:10

andyberry88