Using Middleware to Configure a Service Class

December 2017

I was doing some refactors to an API that I’m building and stumbled across a bucket of duplication.

Note: I’m going to abstract away a lot of the domain for the examples.

I happen to be using explicit route model binding, figured I should share that for some context:

namespace App\Providers;

use App\ExampleModel;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;

class RouteServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Route::bind('exampleModel', function ($value) {
            return ExampleModel::uuid($value)->firstOrFail();
        });

        parent::boot();
    }
}

So here is where I was finding heaps of duplication…

namespace App\Http\Controllers;

use App\Services\Gateway;
use App\Resources\ExampleResource;

class ResourceController extends Controller
{
    public function __invoke()
    {
        $response = app(Gateway::class)
            ->setUser(auth()->user())
            ->setModel(request()->exampleModel)
            ->exampleAction(request()->only(['value_one', 'value_two']))
            ->send();

        return ExampleResource::make($response);
    }
}

This pattern was repeated in several controllers. So what I ended up doing was extracting the configuration into a middleware.

namespace App\Http\Middleware;

use Closure;
use App\Services\Gateway;

class GatewayMiddleware
{
    public function handle($request, Closure $next)
    {
        app(Gateway::class)
            ->setUser(auth()->user())
            ->setModel($request->route('exampleModel'))

        return $next($request);
    }
}

In order for this to work, I needed to ensure that the IOC Container had the Gateway service bound as a singleton so it would always resolve the same instance every time its requested.

namespace App\Providers;

use App\Services\Gateway;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        // $this->app->bind(Gateway::class);
        $this->app->singleton(Gateway::class)
    }
}

Then I was able to refactor all my controllers.

namespace App\Http\Controllers;

use App\Services\Gateway;
use App\Resources\ExampleResource;

class ResourceController extends Controller
{
    public function __invoke()
    {
        $response = app(Gateway::class)
            ->exampleAction(request()->only(['value_one', 'value_two']))
            ->send();

        return ExampleResource::make($response);
    }
}

This feels a lot cleaner to me, now the controllers are really only concerned with the specific details necessary for handling the request.

Now, how do we test this? Check out my post on testing middleware. An Approach to Testing Middleware