Middleware as a Laravel service provider

2020-06-03 #laravel

When you need to set up a service in a Laravel app, service providers are generally the place to be. But, there's one problem with service providers: they're global. This usually doesn't matter, but in multi-section apps this can be problematic.

Consider the following: you want the default date format for your site to be d/m/Y, but the admin panel should display dates as Y-m-d. Chances are you'd write this in your AppServiceProvider:

<?php
 
namespace App\Providers;
 
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
public function boot(Request $request)
{
if ($request->segment(1) === 'admin') {
Carbon::setToStringFormat('Y-m-d');
} else {
Carbon::setToStringFormat('d/m/Y');
}
}
}

It's fine. It works. But something about that URL check feels icky to me. In essence, we're doing something for a certain group of routes. Sound familiar? We've got middleware for that!

Instead of dumping everything in providers, create two pieces of middleware. Where you store them or what you call them doesn't matter. To demonstrate, I created App\Http\Middleware\BootstrapAdmin and App\Http\Middleware\BootstrapWeb.

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Support\Carbon;
 
class BootstrapAdmin
{
public function handle($request, Closure $next)
{
Carbon::setToStringFormat('Y-m-d');
}
}
<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Support\Carbon;
 
class BootstrapWeb
{
public function handle($request, Closure $next)
{
Carbon::setToStringFormat('d/m/Y');
}
}

You might prefer more granular BootstrapCarbonForAdmin and BootstrapCarbonForWeb classes. It's up to you how granular you want to organize your app.

Now we can apply these middleware classes to our routes.

<?php
 
use App\Http\Middleware\BootstrapAdmin;
use App\Http\Middleware\BootstrapWeb;
 
Route::middleware(BootstrapWeb::class)
->group(function () {
//
});
 
Route::prefix('admin')
->middleware(BootstrapAdmin::class)
->group(function () {
//
});

I've been happily using this pattern in large projects. Other examples include registering breadcrumbs and menus, or bootstrapping Inertia.