4

I want all my repositories to be listed in a single service provider, but I don't want them all loaded at once...

Consider the service provider below:

class RepositoryServiceProvider extends ServiceProvider {

    protected $defer = true;

    public function register()
    {
        $this->app->bind(
            'App\Repositories\Contracts\FooRepository',
            'App\Repositories\SQL\FooSQLRepository');

        $this->app->bind(
            'App\Repositories\Contracts\BarRepository',
            'App\Repositories\SQL\BarSQLRepository');

        // and more to be added later...
    }

    public function provides()
    {

        // Will it defer and load all these at once? Or only the one(s) needed?
        return ['App\Repositories\Contracts\FooRepository',
                'App\Repositories\Contracts\BarRepository'];
    }

}

According to the Laravel docs, I can defer the registration of bindings until needed. But does this work for when I've added multiple bindings in a single service provider? Specifically I mean, will it defer and then load all or load only the one needed?

prograhammer
  • 20,132
  • 13
  • 91
  • 118

1 Answers1

5

Laravel will register all bindings, even if only one is needed. The deferred feature actually works pretty simple. First, a map of the entries in provides() and the actual provider is created:

Illuminate\Foundation\ProviderRepository@compileManifest

if ($instance->isDeferred())
{
    foreach ($instance->provides() as $service)
    {
        $manifest['deferred'][$service] = $provider;
    }
    $manifest['when'][$provider] = $instance->when();
}

Then when make() is called in Illuminate\Foundation\Application...

if (isset($this->deferredServices[$abstract]))
{
    $this->loadDeferredProvider($abstract);
}

...and the binding matches one of a deferred provider it will end up here:

Illuminate\Foundation\Application@registerDeferredProvider

$this->register($instance = new $provider($this));

if ( ! $this->booted)
{
    $this->booting(function() use ($instance)
    {
        $this->bootProvider($instance);
    });
}

As you might are able to tell, now the provider is registered as usual which means register() and boot() is called. If you think about it, it's not even possible to load one binding from a service provider and not include the other ones, because it's all done in one method.

lukasgeiter
  • 147,337
  • 26
  • 332
  • 270
  • 2
    This answer makes me sad, because now I need to create a service provider for each repository (since I don't want to load all of them at the same time) – prograhammer Apr 29 '15 at 13:03
  • 2
    I'm in exactly the same situation. It would be great if there were a flag on the bind method. Maybe time for a PR :) – Cobolt May 10 '18 at 05:21