Service Providers in Laravel 11: Main Things You Need To Know

Service Providers in Laravel 11: Main Things You Need To Know
Admin
Wednesday, August 21, 2024 6 mins to read
Share
Service Providers in Laravel 11: Main Things You Need To Know

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?


Quick Theory: Main Purpose of Service Providers

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.


Default "Empty" AppServiceProvider

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.


Example Use-Cases: register() Method

You can register a singleton in the service provider, ensuring only one instance exists throughout your application.

Example 1. Filament: Register Custom Login Page

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

Example 2. Telescope: Register it Only Locally

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);
}
}

Example 3. Mongo: Register a Cache Driver

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);
});
});
}

Example Use-Cases: boot() Method

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:

Example 1. Prevent Lazy Loading

use Illuminate\Database\Eloquent\Model;
 
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}

Example 2. Define Permissions: Gates

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;
});
}

Example 3. API Resource: Remove "data"

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();
}

Example 4. Register Custom Blade Components

Manually registering Blade components.

use Illuminate\Support\Facades\Blade;
 
public function boot(): void
{
Blade::component('package-alert', Alert::class);
}

Example 5. Blade Component: Customize Namespace

Or, autoloading component classes by convention.

use Illuminate\Support\Facades\Blade;
 
public function boot(): void
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

Example 6. Polymorphic Relation: MorphMap

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.


"Obsolete" Service Providers: Laravel 11 vs Older Versions

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:

  • There's no const HOME: instead, the route's name should be used
  • The routes are set in the bootstrap/app.php file as parameters in the withRouting() method
  • Also, by default, there are no API routes: you need to install them with php 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.


You Can Still Create Custom 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.


Service Providers for Packages

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.