Let's try to understand Laravel Serivice Providers in simple terms

Published Apr 16, 2023 on Laravel by Al Imran Ahmed

Laravel service provider is one of the most important parts of laravel but at the same time it's misunderstood by many and most of the beginners afraid of it. Naturally, we are afraid of things we don't understand. So, I believe one reason of being afraid of Service Provider is due to not having a clear understanding of this. In this article I will try to explain what, why and how of Laravel Service Provider in simple terms so that even a beginner can understand. Let's dive in...

What is service provider?

Let's forget about service provider for now. Imagine in web.php route file of our Laravel application, we have a route called /test which will be handled TestController::__invoker as below

//in web.php

Route::get('/test', TestController::class);

When we make a get request /test of our Laravel application, what should happen? What first thing will it do? Will it directly execute the __invoke() method of the TestController? No. Because before even executing this TestController, Laravel actually does a lot of things for us. Part of which you can say Bootstrapping. As a web developer and a non-native English speaker, the word Boostrap might seem a bit confusing. Like many of you I also came across the word Bootstrap for the first time from the Bootstrap frontend framework. But after spending more time with web development and English language I came to know that, Bootstrap basically means the loading process of any system. In Laravel, when we make a request, first thing it does is loading all the necessary classes that are required for further processing. For instance, we are saying that our /test request will be handled by TestController::__invoker of our application. How does our application even know that? Somewhere in the application it has to be defined how route should be resolved, right? That somewhere is actually the bootstrapping or loading process. A part of this bootstrapping process is loading the Service Provider classes as these are also the necessary classes to proceed next. When we call our Laravel application from Http or Console, it always loads the service providers class first. We can see the list of service providers that are being loaded in providers key of the array in config/app.php file. When we install fresh a Laravel application, we can only see the providers we will mostly interact with. Here is the last part of those list of providers in config/app.php file:

'providers' => [
    //...
    
    /*
     * Application Service Providers...
     */
    App\Providers\AppServiceProvider::class,
    App\Providers\AuthServiceProvider::class,
    // App\Providers\BroadcastServiceProvider::class,
    App\Providers\EventServiceProvider::class,
    App\Providers\RouteServiceProvider::class,
]

All those service providers are just simple PHP classes, nothing complicated. One special thing about them is, their register() and boot() methods is called when our application is loaded. You will see that many of these classes don't even have the register() method shown, but they have it in their parent class. Now, before going to what these two methods do, we should see in what sequence those methods are called. Here is the sequence:

App\Providers\AppServiceProvider::register()
App\Providers\AuthServiceProvider::register()
App\Providers\EventServiceProvider::register()
App\Providers\RouteServiceProvider::register()

App\Providers\AppServiceProvider::boot()
App\Providers\AuthServiceProvider::boot()
App\Providers\EventServiceProvider::boot()
App\Providers\RouteServiceProvider::boot()

Notice, what is happening, it's called every register() methods of service providers in the sequence we have them in our config/app.php file. After finish calling all register() methods, it calls every boot() method of every service provider, in the same sequence! That it, we have some simple PHP classes that have these two methods, they are being called by Laravel in this sequence. Now, it's up to us what we can and want to do in those methods. As simple as that!

Why service provider?

Now we know we have two methods in service provider's register() and boot(). All register() methods are called first then the boot() methods. Why do we even need them? What are we supposed to do there? Well, technically we can do anything we want during bootstrapping our application, which means before our middleware, request, controller etc. are being called. What are the ideal things we do here then? We can register Model observer, define global configuration for pagination, register service container of a package etc. We can think the register() method as the place to register any dependency injection mechanism or service container and the boot() method as object-oriented way of specifying any configuration of our application. Yes, we already have configuration PHP files insides configs/ directory that contains only array of configurations. But this boot() method gives us the controller of calling a method, instantiating a class etc. This is the convention, but as I said technically we can do anything we want here before we want to execute our middleware, validation, controller. Let me give you an example. Imagine we have a service called Sms service. That has two class RealSms and FakeSms both of them implements an interface called SmsInterface. The SmsInterface looks as below:

<?php

namespace App\Services\Sms;

interface SmsInterface
{
    public function send(string $phone, string $text): string;
}

Now, we want to register which Sms service will be used when we resolve the SmsInterface. Where do we register it? Yes, you guessed it right we can register it in the register() method of our Service Provider like below in AppServiceProvider:

//AppServiceProvider.php

public function register(): void
{
    $this->app->bind(SmsInterface::class, RealSms::class);
}

Why we did this here? Because we will need this to be resolved in one of our Controllers:

class TestController extends Controller
{
    public function __invoke(Request $request, SmsInterface $sms)
    {
        $sms->send("+xxxxxxxxxxxx", "Test message");
    }
}

You see, in the __invoke() method we are injecting SmsInterface but we need to tell Laravel how this will be resolved. We have to do it somewhere before the TestController is called. That somewhere doesn't always have to be a service provider but can be a service provider and conventionally this is the place where register our service container in Laravel.

When to make your own service provider?

In the previous section we have seen that we register our SmsInterface in the register() method of AppServiceProvider class. What if our Sms service provider need to register many more service containers? What if our application has many more service like Sms service? You can imagine this AppServiceProvider will get messy pretty quickly. For our Sms service we only have one class to register. At this point, I think it's fine to register in AppServiceProvider. So, yeah when you see your service demands a new place to register it's all service container and configuration in one Service Provider, that's the time you should create a new Service Provider for that service.

How to define your own service provider?

Creating or defining a new service provider in Laravel application is pretty simple. Laravel even comes with a command to create a Service Provider. We can create a Service Provider called SmsServiceProvider using the command below:

php artisan make:provider SmsServiceProvider

After executing this command, we will se that we have a new class file in app/Providers directory called SmsServiceProvider.php and that class contains two friendly methods register() and boot(). We can move our

$this->app->bind(SmsInterface::class, RealSms::class);

and any other service container registration related to Sms service to the register() method of SmsServiceProvider as below:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class SmsServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     */
    public function register(): void
    {
        $this->app->bind(SmsInterface::class, RealSms::class);
        // Or any other class binding related to Sms service!
    }

    /**
     * Bootstrap services.
     */
    public function boot(): void
    {
        //
    }
}

That's it about Laravel's service provider. I hope now you understand clearly what, why and how to use Laravel's service provider. Yeah, it's just a class that load while bootstrapping our Laravel application.

Comments(0)

+
No noise, unsubscribe anytime!
© 2024 Al Imran Ahmed
Proudly build with: Larablog
Contact