2

In my service provider I bind a class with singleton:

public function register()
{
    $this->app->singleton('ResourceContainer', function($app){
        return new ResourceContainer();
    });
}

The laravel doumentation says that this class will be resolved one time and the same object will be returned:

The singleton method binds a class or interface into the container that should only be resolved one time. Once a singleton binding is resolved, the same object instance will be returned on subsequent calls into the container:

But in my app the constructor of ResourceContainer is called twice.

I want to call this instance in my boot method of the serviceprovider:

public function boot()
{
    $resourceContainer = $this->app->make('ResourceContainer');

And I inject the class in a controller:

public function index(ResourceContainer $container, $resource){

When I debug, the constructor of ResourceContainer is called twice. I get a different object in my controller than in the boot method of the service provider.

Jochem Gruter
  • 2,813
  • 5
  • 21
  • 43

2 Answers2

3

The reason is you are requesting different objects. It basically boils down to this two lines of code:

$resourceContainer = $this->app->make('ResourceContainer');
public function index(ResourceContainer $container) { ... } // simplified

As you most likely do not have the ResourceContainer class sitting in the global namespace, but probably somewhere like App\Services\ResourceContainer, this is the name that will be requested from the service container. So type-hinting a class will actually call $this->app->make('App\Services\ResourceContainer') in the background due to the class being in that namespace.

The solution is therefore rather simple: Register the singleton under its class name and change the manual call to make($class). The type-hint will work out of the box.

public function register()
{
    $this->app->singleton(ResourceContainer::class, function($app) {
        return new ResourceContainer();
    });
}
Namoshek
  • 6,394
  • 2
  • 19
  • 31
1

You can eliminate this problem by using the ::class syntax instead:

$this->app->singleton(ResourceContainer::class, function($app){
    return new ResourceContainer();
});

And use it the same way with make():

$resourceContainer = $this->app->make(ResourceContainer::class);

The problem is that you are dealing with two different strings:

'ResourceContainer'
'Foo\Bar\ResourceContainer'

The first is the key (string) you bound the singleton to in the container, and the second is the fully-qualified class name. Behind the scenes, Laravel's service container is basically a key=>value array which maps string keys to function values, so the different strings end up being important.

When you bind 'ResourceContainer', then $this->app->make('ResourceContainer') will correctly resolve your singleton and behave as you were expecting.

However, when you use automatic dependency injection for methods like this:

public function index(ResourceContainer $container, $resource) {}

Laravel uses reflection to inspect the method parameters, so it sees the parameter type as the fully-qualified class name 'Foo\Bar\ResourceContainer' (not 'ResourceContainer'). When it checks the container for this string it doesn't find a match. Another feature of Laravel is that it is "smart" enough to resolve simple dependencies even if they were not explicitly bound in the container, so here it will actually determine that it can build the dependency (even though it didn't find a match in the container) and creates new instance of the class, instead of referencing the singleton, because it is acting as if no bindings were found.

The ::class syntax solves all of this because it returns the fully-qualified class name, so everything will reference the same key in the container.

Travis Britz
  • 5,094
  • 2
  • 20
  • 35