• Reading time ~ 3 min
  • 03.06.2022

For those who haven't actively used Service Providers in Laravel, it's a mystical "term": what "service" do they actually "provide", and how exactly does it all work? I will explain it in this article.


Default Laravel Service Providers

Let's start with the default service providers included in Laravel, they are all in the app/Providers folder:

  • AppServiceProvider
  • AuthServiceProvider
  • BroadcastServiceProvider
  • EventServiceProvider
  • RouteServiceProvider

They are all PHP classes, each related to its topic: general "app", Auth, Broadcasting, Events, and Routes. But they all have one thing in common: the boot() method.

Inside of that method, you can write any code related to one of those sections: auth, events, routes, etc. In other words, Service Providers are just classes to register some global functionality.

They are separated as "providers" because they are executed really early in the Application Lifecycle, so it is convenient something global here before the execution script comes to Models or Controllers.

The most amount of functionality is in the RouteServiceProvider, let's take a look at its code:

class RouteServiceProvider extends ServiceProvider
{
    public const HOME = '/dashboard';
 
    public function boot()
    {
        $this->configureRateLimiting();
 
        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->group(base_path('routes/api.php'));
 
            Route::middleware('web')
                ->group(base_path('routes/web.php'));
        });
    }
 
    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
        });
    }
}

This is the class where route files are configured, with routes/web.php and routes/api.php included by default.Notice that for the API there are also different configurations: endpoint prefix /api and middleware api for all the routes.

You can edit those Service Providers however you want, they are not in the /vendor folder. Typical customization of this file happens when you have a lot of routes and you want to separate them in your custom file.You create routes/auth.php and put the routes there, and then you "enable" that file in the boot() method of the RouteServiceProvider, just add the third sentence:

`Route::middleware('web') // or maybe you want another middleware?
    ->group(base_path('routes/auth.php'));

Other default service providers have other functionality, you can analyze them by yourself.Except for AppServiceProvider, it is empty, like a placeholder for us to add any code related to some global application settings.

One popular example of adding code to the AppServiceProvider is about disabling the lazy loading in Eloquent.To do that, you just need to add two lines into the boot() method:

// app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Model;
 
public function boot()
{
    Model::preventLazyLoading(! $this->app->isProduction());
}

This will throw an exception if some relationship model isn't eager loaded, which causes a so-called N+1 query problem with performance.


When Service Providers Are Executed?

If you look at the official docs about request lifecycle, these are the things executed in the very beginning:

  • public/index.php
  • bootstrap/app.php
  • app/Http/Kernel.php and its Middlewares
  • Service Providers: exactly our topic of this article

Which providers are loaded? It's defined in the config/app.

return [
 
	// ... other configuration values
 
    'providers' => [
 
        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
 
        // ... other framework providers from /vendor
        Illuminate\Validation\ValidationServiceProvider::class,
        Illuminate\View\ViewServiceProvider::class,
 
        /*
         * PUBLIC Service Providers - the ones we mentioned above
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
 
    ],
 
];

Quite a typical example is configuration related to Blade views.If you want to create your Blade directive, you can add that code into any service provider's boot() method, including the default AppServiceProvider, but quite often developers create a separate ViewServiceProvider.


php artisan make:provider ViewServiceProvider

namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
 
class ViewServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
 
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}
use Illuminate\Support\Facades\Blade;
 
public function boot()
{
    Blade::directive('datetime', function ($expression) {
        return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
    });
}
use App\View\Composers\ProfileComposer;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
 
class ViewServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // Using class based composers...
        View::composer('profile', ProfileComposer::class);
 
        // Using closure based composers...
        View::composer('dashboard', function ($view) {
            //
        });
    }
}

return [ // ... other configuration values   'providers' => [   App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class,   // Add your provider here App\Providers\ViewServiceProvider::class, ], ];


namespace App\Providers;   use App\Http\Components\AdComponent; use Illuminate\Support\Facades\Blade; use Illuminate\Support\ServiceProvider;   class BladeComponentServiceProvider extends ServiceProvider { public function boot() { Blade::component('ad', AdComponent::class);   Blade::component('front.components.inputField', 'input-field'); Blade::component('front.components.submitButton', 'submit-button'); Blade::component('front.components.textarea', 'textarea'); Blade::component('front.components.textarea', 'textarea'); Blade::component('front.components.shareButton', 'share-button'); Blade::component('front.components.lazy', 'lazy'); Blade::component('front.components.postHeader', 'post-header');   Blade::component('front.layouts.app', 'app-layout'); } }

namespace App\Providers;
 
use App\Helpers\CollectionHelper;
use Illuminate\Support\Collection;
use Illuminate\Support\ServiceProvider;
 
class MacroServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        if (! Collection::hasMacro('sortByCollator')) {
            Collection::macro('sortByCollator', function ($callback, $options = \Collator::SORT_STRING, $descending = false) {
                /** @var Collection */
                $collect = $this;
 
                return CollectionHelper::sortByCollator($collect, $callback, $options, $descending);
            });
        }
 
        if (! Collection::hasMacro('groupByItemsProperty')) {
            Collection::macro('groupByItemsProperty', function ($property) {
                /** @var Collection */
                $collect = $this;
 
                return CollectionHelper::groupByItemsProperty($collect, $property);
            });
        }
 
        if (! Collection::hasMacro('mapUuid')) {
            Collection::macro('mapUuid', function () {
                /** @var Collection */
                $collect = $this;
 
                return $collect->map(function ($item) {
                    return $item->uuid;
                })->toArray();
            });
        }
    }
}
 
namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;
use App\Helpers\FileUpload\UploadComponents;
 
class DashboardComponentsServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
 
    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        Blade::directive('uploadForm', function () {
            $component = UploadComponents::getUploadForm();
            $html = '<?php echo \'' . $component . '\'; ?>';
 
            return ('<?php echo "' . $component . '"; ?>');
        });
    }
}

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

ABOUT

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

About author CrazyBoy49z
WORK EXPERIENCE
Contact
Ukraine, Lutsk
+380979856297