Introduction

Service providers are Laravel's startup hooks: every major feature is bootstrapped through one. This lesson explains the register() and boot() methods, how they differ, and when you should write your own provider.

Key Concepts

  • Service provider: A class that registers bindings and boots features during application start-up.
  • register(): Binds classes into the container — must not resolve other services yet.
  • boot(): Runs after every provider has registered — safe to use other services.
  • Service container: Laravel's dependency injection container that resolves type-hinted dependencies.
  • Deferrable provider: A provider whose loading is delayed until one of its services is actually needed.

Real World Context

Every third-party Laravel package you install ships a provider that wires up its facades, routes, config, and migrations. Understanding providers is the difference between 'I installed a package' and 'I know what it added to my app'.

Deep Dive

Service providers are the central place for configuring and bootstrapping your Laravel application. Every major Laravel feature is bootstrapped via a service provider.

What Are Service Providers?

Service providers are classes responsible for:

  1. Registering things into the service container
  2. Booting the application with necessary setup
Laravel Application Boot Process
┌─────────────────────────────────────────┐
│  1. Load Configuration                  │
│  2. Register Service Providers          │
│     - register() method called          │
│  3. Boot Service Providers              │
│     - boot() method called              │
│  4. Handle Request                      │
└─────────────────────────────────────────┘

Creating a Service Provider

Generate a new provider:

bash
php artisan make:provider RiakServiceProvider

This creates app/Providers/RiakServiceProvider.php:

php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        // Bind services to the container
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // Run after all providers are registered
    }
}

The register() Method

Use register() to bind things into the service container:

php
public function register(): void
{
    // Bind a class
    $this->app->bind(PaymentGateway::class, function ($app) {
        return new StripeGateway(config('services.stripe.key'));
    });

    // Bind a singleton (one instance shared)
    $this->app->singleton(ReportGenerator::class, function ($app) {
        return new ReportGenerator();
    });
}

Rule: Only bind things in register(). Don't use other services here—they might not be registered yet.

The boot() Method

Use boot() after all providers are registered:

php
public function boot(): void
{
    // Configure view composers
    View::composer('dashboard', function ($view) {
        $view->with('notifications', auth()->user()->notifications);
    });

    // Register custom validation rules
    Validator::extend('phone', function ($attribute, $value) {
        return preg_match('/^[0-9]{10}$/', $value);
    });

    // Publish package assets
    $this->publishes([
        __DIR__.'/../config/package.php' => config_path('package.php'),
    ]);
}

Built-in Service Providers

Laravel includes many providers in bootstrap/providers.php:

php
<?php

return [
    App\Providers\AppServiceProvider::class,
];

Laravel 13 uses automatic package discovery, so most providers are loaded automatically.

The AppServiceProvider

Your main application provider at app/Providers/AppServiceProvider.php:

php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\URL;
use Illuminate\Pagination\Paginator;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Register application bindings
    }

    public function boot(): void
    {
        // Force HTTPS in production
        if ($this->app->environment('production')) {
            URL::forceScheme('https');
        }

        // Prevent N+1 queries by failing fast outside production
        Model::preventLazyLoading(! $this->app->isProduction());

        // Opt in to Bootstrap 5 pagination views instead of the default Tailwind
        Paginator::useBootstrapFive();
    }
}

Registering Your Provider

Add custom providers to bootstrap/providers.php:

php
<?php

return [
    App\Providers\AppServiceProvider::class,
    App\Providers\RiakServiceProvider::class,  // Add your provider
];

Deferred Providers

For performance, defer loading until needed:

php
<?php

namespace App\Providers;

use App\Services\Riak\Connection;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(Connection::class, function () {
            return new Connection(config('riak'));
        });
    }

    /**
     * Get the services provided by the provider.
     */
    public function provides(): array
    {
        return [Connection::class];
    }
}

The Service Container

Service providers work with Laravel's service container. The container manages class dependencies and performs dependency injection.

php
// When you type-hint a class, Laravel resolves it from the container
class UserController extends Controller
{
    public function __construct(
        private PaymentGateway $gateway  // Auto-injected!
    ) {}

    public function charge()
    {
        $this->gateway->charge(100);
    }
}

Common Pitfalls

  1. Resolving services from register() — Other providers may not be booted yet. Move the logic to boot().
  2. Putting route definitions in arbitrary providers — Keep routes in routes/*.php unless you have a specific reason to override.

Best Practices

  1. Group domain bootstrapping in one provider — DomainServiceProvider for your Stripe bindings, morph map, and macros is clearer than scattering them.
  2. Use DeferrableProvider for heavy, rarely used services — It avoids loading code every request.

Summary

  • Service providers bootstrap Laravel features during start-up.
  • register() binds classes; boot() uses them.
  • Register custom providers in bootstrap/providers.php.
  • DeferrableProvider delays loading until first use.
  • The service container resolves constructor and method dependencies automatically.
✓ Completed