Service Providers have been the core functionality of Laravel since the beginning. However, in Laravel 11, the list of providers changed, with only AppServiceProvider
left. So, what do developers need to know now?
The primary purpose of the service providers is to be the central place for registering classes or various global settings.
In my opinion, it should have been called "Class Configuration" or something similar, not a Service Provider, to avoid confusion. But we need to work with what we have, right?
In the AppServiceProvider
, you would typically register singletons, event listeners, and optional extra configurations for various Laravel features like API resources, polymorphic relations, etc.
After you install Laravel 11, the AppServiceProvider
looks like this:
app/Providers/AppServiceProvider.php:
namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider{ /** * Register any application services. */ public function register(): void { // } /** * Bootstrap any application services. */ public function boot(): void { // }}
What's the difference between the register()
and boot()
methods?
The short explanation is that the register()
method for all providers is called earlier than the boot()
method.
register()
: here, you can add simple binding and singletons.boot()
: this is where you can perform tasks that may depend on other registered providers. Here, you can add macros, event listeners, define gates, etc.Now, let's look at the usage examples for both methods.
You can register a singleton in the service provider, ensuring only one instance exists throughout your application.
We overwrite the response using Singleton here with a custom response class.
public function register(): void{ $this->app->singleton( \Filament\Http\Responses\Auth\Contracts\LoginResponse::class, \App\Http\Responses\LoginResponse::class );}
Notice: You don't know much about Singleton? We cover it in the course Design Patterns in Laravel
Here's another example from the Laravel Telescope package if you want to use the Telescope only in a local environment.
public function register(): void{ if ($this->app->environment('local')) { $this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class); $this->app->register(TelescopeServiceProvider::class); }}
Another example is for registering a cache driver.
use App\Extensions\MongoStore;use Illuminate\Contracts\Foundation\Application;use Illuminate\Support\Facades\Cache; public function register(): void{ $this->app->booting(function () { Cache::extend('mongo', function (Application $app) { return Cache::repository(new MongoStore); }); });}
Next, after the register()
method is executed, Laravel processes the boot()
method for all providers.
Here are the examples of what we can do here:
use Illuminate\Database\Eloquent\Model; public function boot(): void{ Model::preventLazyLoading(! $this->app->isProduction());}
The definition of Gates is typically put in the AppServiceProvider
, too.
use App\Models\Post;use App\Models\User;use Illuminate\Support\Facades\Gate; public function boot(): void{ Gate::define('update-post', function (User $user, Post $post) { return $user->id === $post->user_id; });}
When working with APIs, you may remove response wrapping with the data
key.
use Illuminate\Http\Resources\Json\JsonResource; public function boot(): void{ JsonResource::withoutWrapping();}
Manually registering Blade components.
use Illuminate\Support\Facades\Blade; public function boot(): void{ Blade::component('package-alert', Alert::class);}
Or, autoloading component classes by convention.
use Illuminate\Support\Facades\Blade; public function boot(): void{ Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');}
The last example is forcing the use of a name other than a fully qualified class name when storing a "type" for the polymorphic relationship.
use Illuminate\Database\Eloquent\Relations\Relation; public function boot(): void{ Relation::enforceMorphMap([ 'post' => 'App\Models\Post', 'video' => 'App\Models\Video', ]);}
These are only a few use-cases. For more examples, just click here to see how many times "AppServiceProvider" is mentioned in the official Laravel docs.
Before Laravel 11, there were five service providers in the default installation in the app/Providers
folder:
Each service provider was responsible for its own configuration. For example, RouteServiceProvider
had everything about routes, and EventServiceProvider
had events, listeners, and Eloquent observers.
Since Laravel 11, service providers have been given a clean-up. Everything is now "baked" into the framework and can be changed in one file, bootstrap/app.php.
Example: in the older RouteServiceProvider
we had the rate limit setter and providing routes. Also, a constant HOME
with a link to some page.
class RouteServiceProvider extends ServiceProvider{ public const HOME = '/home'; public function boot(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); }); $this->routes(function () { Route::middleware('api') ->prefix('api') ->group(base_path('routes/api.php')); Route::middleware('web') ->group(base_path('routes/web.php')); }); }}
In Laravel 11, these were moved away:
HOME
: instead, the route's name should be usedbootstrap/app.php
file as parameters in the withRouting()
methodphp artisan install:api
bootstrap/app.php:
return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // }) ->withExceptions(function (Exceptions $exceptions) { // })->create();
With that cleanup, there is no longer a need for RouteServiceProvider
.
So, in Laravel 11, you will usually only use the AppServiceProvider
without any other Service Providers.
Even if you get only one service provider by default, you can still create as many as you need. If you want to separate logic from different providers, you still can do that.
Let's say you need a few View macros, shares, etc. Using an artisan command, you can create a ViewServiceProvider
.
php artisan make:provider ViewServiceProvider
In the ViewServiceProvider
, add all the logic that you need.
Remember to register all your service providers in the bootstrap/providers.php
file.
bootstrap/providers.php:
return [ App\Providers\AppServiceProvider::class, App\Providers\ViewServiceProvider::class, ];
A great example of having multiple service providers is how Filament uses them. Each Service Provider is for a separate panel with its set of global configurations see the Filament docs.
A separate topic for service providers is creating Laravel packages. In that case, having a service provider is a must because it is a connection between your package and Laravel.
In the Service Provider, you provide your package config file, load migrations, routes, language files, etc.
Laravel can automatically load your package service provider if it is defined in the composer.json
,
"extra": { "laravel": { "providers": [ "Barryvdh\\Debugbar\\ServiceProvider" ] }},
Want to learn more about building Laravel packages? Check out our course: How to Create Laravel Package: Step-by-Step Example.