> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Uitgestelde service providers

> Hoe je met de interface DeferrableProvider services lui laadt. Een onopvallende maar belangrijke functie die direct bijdraagt aan de prestatie-optimalisatie van packages.

Service providers die bij het opstarten van Laravel worden geregistreerd, worden elke keer geladen — zelfs als hun functionaliteit tijdens het request geen enkele keer wordt gebruikt. Uitgestelde service providers (deferred service providers) lossen dit probleem op: het laden wordt uitgesteld totdat de service daadwerkelijk wordt gebruikt.

<Info>
  Deze pagina gaat uit van kennis van de [basis van packageontwikkeling](/nl/advanced/package-development). We raden aan eerst de basiswerking van service providers te begrijpen.
</Info>

## Waarom uitgestelde providers nodig zijn

Bij een gewone service provider worden `register()` en `boot()` bij elk request aangeroepen. Het is verspilling om functionaliteit die niet op elke pagina wordt gebruikt — e-mail, queues, cache — telkens te initialiseren.

```php theme={null}
// Bij deze provider wordt register() bij elk request aangeroepen
class ReportServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Bindt de zware rapportageservice elke keer opnieuw
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make(PdfRenderer::class),
                $app->make(ChartRenderer::class),
                $app->make(DataExporter::class),
            );
        });
    }
}
```

Maak je deze provider uitgesteld, dan wordt hij alleen geïnitialiseerd bij requests die daadwerkelijk een rapport genereren.

***

## De DeferrableProvider-interface

`Illuminate\Contracts\Support\DeferrableProvider` is een simpele interface met één methode: `provides()`.

```php theme={null}
namespace Illuminate\Contracts\Support;

interface DeferrableProvider
{
    public function provides();
}
```

Alleen al door deze interface te implementeren, gaat de provider in uitgestelde modus.

***

## Basisimplementatie

De implementatie van een uitgestelde provider bestaat uit drie stappen.

<Steps>
  <Step title="DeferrableProvider implementeren">
    ```php theme={null}
    use Illuminate\Contracts\Support\DeferrableProvider;
    use Illuminate\Support\ServiceProvider;

    class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
    {
        // ...
    }
    ```
  </Step>

  <Step title="Services binden in register()">
    Net als bij een gewone provider schrijf je de bindings in `register()`.

    ```php theme={null}
    public function register(): void
    {
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make(PdfRenderer::class),
                $app->make(ChartRenderer::class),
                $app->make(DataExporter::class),
            );
        });

        $this->app->singleton(ReportRepository::class, function ($app) {
            return new ReportRepository($app->make('db'));
        });
    }
    ```
  </Step>

  <Step title="De geregistreerde services teruggeven in provides()">
    `provides()` moet **alle services** teruggeven die je in `register()` hebt gebonden. Op basis van deze lijst bepaalt Laravel "bij welke gevraagde service deze provider geladen moet worden".

    ```php theme={null}
    public function provides(): array
    {
        return [
            ReportGenerator::class,
            ReportRepository::class,
        ];
    }
    ```
  </Step>
</Steps>

***

## Hoe het servicemanifest werkt

Laravel genereert bij het opstarten een manifestbestand: `bootstrap/cache/services.php`. In dit bestand staat de lijst met services die uitgestelde providers aanbieden.

```php theme={null}
// Voorbeeld van bootstrap/cache/services.php (automatisch gegenereerd)
return [
    'providers' => [
        // Dezelfde lijst als bootstrap/providers.php
    ],
    'eager' => [
        // Direct geladen providers
        App\Providers\AppServiceProvider::class,
    ],
    'deferred' => [
        // Mapping servicenaam => providerklasse
        'App\Services\ReportGenerator' => ReportServiceProvider::class,
        'App\Repositories\ReportRepository' => ReportServiceProvider::class,
        'cache'     => Illuminate\Cache\CacheServiceProvider::class,
        'cache.store' => Illuminate\Cache\CacheServiceProvider::class,
        'queue'     => Illuminate\Queue\QueueServiceProvider::class,
    ],
    'when' => [],
];
```

Dankzij dit manifest weet Laravel — zonder bestanden te laden — "welke provider deze service aanbiedt". De echte provider wordt pas geladen wanneer de betreffende service voor het eerst wordt geresolved.

<Tip>
  Genereer het manifest opnieuw wanneer je providers toevoegt of wijzigt.

  ```shell theme={null}
  php artisan optimize:clear
  # of
  php artisan clear-compiled
  ```
</Tip>

### Interne flow

```mermaid theme={null}
sequenceDiagram
    participant App as Laravel-<br>app
    participant Container as Service-<br>container
    participant Manifest as services.php-<br>manifest
    participant Provider as ReportService<br>Provider

    App->>Manifest: Manifest inladen bij het opstarten
    Manifest-->>App: Lijst met deferred services teruggeven
    Note over App: Op dit moment is de provider nog niet geladen

    App->>Container: $app->make(ReportGenerator::class)
    Container->>Manifest: Controleren of het een deferred service is
    Manifest-->>Container: ReportServiceProvider biedt deze aan
    Container->>Provider: register() + boot() aanroepen
    Provider-->>Container: Bindings registreren
    Container-->>App: Instantie van ReportGenerator teruggeven
```

***

## Het belang van de provides()-methode

**Ontbreekt** een service in `provides()`, dan wordt die service nooit geresolved.

```php theme={null}
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, fn ($app) => new ReportGenerator());

    // Toegevoegde binding
    $this->app->singleton('report', fn ($app) => $app->make(ReportGenerator::class));
}

public function provides(): array
{
    return [
        ReportGenerator::class,
        'report',  // ← vergeet ook bindings met een string-sleutel niet
    ];
}
```

Gebruik je de properties `$bindings` / `$singletons`, dan neem je die op dezelfde manier op in `provides()`.

```php theme={null}
class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public $singletons = [
        AnalyticsClient::class => DefaultAnalyticsClient::class,
    ];

    public function provides(): array
    {
        // Geef alle sleutels terug die in $singletons staan
        return [
            AnalyticsClient::class,
        ];
    }
}
```

***

## Beperkingen van uitgestelde providers

Uitgestelde providers zijn geschikt voor providers die **alleen bindings registreren in de container**. Providers die het volgende in `boot()` doen, kun je niet uitstellen:

| Beperking                                          | Reden                                                                            |
| -------------------------------------------------- | -------------------------------------------------------------------------------- |
| Routes registreren                                 | Routes worden bij het opstarten van de app geresolved; uitgesteld werkt dit niet |
| Globale middleware registreren                     | Registratie moet plaatsvinden vóór de verwerking van het request                 |
| Eventlisteners registreren (die altijd nodig zijn) | Werkt niet als de registratie niet plaatsvindt vóórdat het event afgaat          |
| Blade-directives toevoegen                         | Registratie moet plaatsvinden vóór het compileren van de views                   |

<Warning>
  Je kunt in een uitgestelde provider wel een `boot()`-methode schrijven, maar de inhoud wordt pas uitgevoerd wanneer de service wordt geresolved. Zet je "altijd noodzakelijke verwerking" zoals routes of middleware in `boot()`, dan krijg je onverwacht gedrag.
</Warning>

***

## De when()-methode — registratie via event-triggers

Met de methode `when()` kun je de provider laten registreren wanneer een specifiek event afgaat. Dit is bruikbaar voor providers die alleen in een bepaalde context nodig zijn, zoals jobverwerking.

```php theme={null}
class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(ReportGenerator::class, fn () => new ReportGenerator());
    }

    public function provides(): array
    {
        return [ReportGenerator::class];
    }

    /**
     * Laad de provider niet alleen bij het resolven van de servicebinding,
     * maar ook wanneer het opgegeven event afgaat.
     */
    public function when(): array
    {
        return [
            \App\Events\ReportRequested::class,
        ];
    }
}
```

Gaat een event af dat je via `when()` hebt teruggegeven, dan wordt de provider geladen — ook als de service niet direct wordt geresolved.

***

## Toepassing bij packageontwikkeling

Ook bij distributie als third-party package dragen uitgestelde providers bij aan de prestaties van de applicatie van de gebruiker.

### Aanbevolen patroon

```php theme={null}
namespace Acme\Analytics;

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(__DIR__.'/../config/analytics.php', 'analytics');

        $this->app->singleton(AnalyticsManager::class, function ($app) {
            return new AnalyticsManager($app->make('config')->get('analytics'));
        });

        $this->app->singleton('analytics', fn ($app) => $app->make(AnalyticsManager::class));
    }

    public function boot(): void
    {
        // Gebruik boot() alleen voor het registreren van publishes
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/../config/analytics.php' => config_path('analytics.php'),
            ], 'analytics-config');
        }
    }

    public function provides(): array
    {
        return [
            AnalyticsManager::class,
            'analytics',
        ];
    }
}
```

<Info>
  `mergeConfigFrom()` controleert intern of de configuratie gecachet is en is dus veilig aan te roepen binnen `register()` van een uitgestelde provider. Is de configuratie al gecachet, dan doet de methode niets.
</Info>

### Registratie van Artisan-commands scheiden met `runningInConsole()`

Commandregistratie is alleen nodig wanneer Artisan draait, dus je vertakt met `runningInConsole()`. Maar wil je een provider die commands aanbiedt uitstellen, neem dan ook de commandklassen op in `provides()` of maak een aparte provider alleen voor commands.

```php theme={null}
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            AnalyticsFlushCommand::class,
        ]);
    }
}
```

***

## Voorbeelden uit de Laravel-core

Veel van de core providers van Laravel zijn uitgestelde providers. Het ontwerp voorkomt dat alle services die niet bij elk request nodig zijn direct worden geladen.

| Provider                     | Aangeboden services                     |
| ---------------------------- | --------------------------------------- |
| `CacheServiceProvider`       | `cache`, `cache.store`, `RateLimiter`   |
| `QueueServiceProvider`       | `queue`, `queue.worker`, `queue.failer` |
| `MailServiceProvider`        | `mail.manager`, `mailer`                |
| `RedisServiceProvider`       | `redis`, `redis.connection`             |
| `HashServiceProvider`        | `hash`, `hash.driver`                   |
| `ValidationServiceProvider`  | `validator`, `validation.presence`      |
| `TranslationServiceProvider` | `translator`                            |
| `BroadcastServiceProvider`   | `Broadcast`                             |

In een applicatie met alleen API-endpoints komt het voor dat de `MailServiceProvider` en `BroadcastServiceProvider` tijdens een request geen enkele keer worden geladen.

***

## Criteria: wel of niet uitstellen?

<AccordionGroup>
  <Accordion title="Services die geschikt zijn voor uitstel">
    * Services die niet bij elk request worden gebruikt (e-mail, rapporten, externe API-clients, enzovoort)
    * Services waarvan de initialisatie externe verbindingen of het inlezen van bestanden vereist
    * Services met een zware objectgraaf
    * Providers die commands aanbieden die alleen via de CLI worden gebruikt
  </Accordion>

  <Accordion title="Services die niet geschikt zijn voor uitstel">
    * Providers die routes registreren (`loadRoutesFrom` en dergelijke)
    * Providers die altijd actieve middleware of exception handlers registreren
    * Providers die globale scopes of observers van Eloquent registreren
    * Lichte services die in het merendeel van de requests worden gebruikt (de overhead van uitstel kan dan groter zijn)
  </Accordion>
</AccordionGroup>

***

## Gerelateerde pagina's

<Columns cols={2}>
  <Card title="Basis van packageontwikkeling" icon="box" href="/nl/advanced/package-development">
    Uitleg over het ontwikkelen van Laravel-packages met de service provider als kern.
  </Card>

  <Card title="Versiecompatibiliteit van packages beheren" icon="layers" href="/nl/advanced/package-versioning">
    Onderhoudsstrategieën voor packages die meegroeien met major versies van Laravel en PHP.
  </Card>
</Columns>


## Related topics

- [Service providers](/nl/service-providers.md)
- [FAQ over de nieuwe appstructuur van Laravel 11+](/nl/advanced/app-structure-faq.md)
- [Request lifecycle](/nl/lifecycle.md)
- [Laravel-packages ontwikkelen](/nl/advanced/package-development.md)
- [Applicatiestructuur van Laravel 11 en later](/nl/advanced/app-structure.md)
