RoboRobok's avatar

Why are some container bindings not interfaces?

In Laravel, some container bindings happen to be an interface name and some are just keys, like cache, db and session. Why these key-based ones don't stick to an interface?

For my understanding, automatic resolving uses container, like

class FooController
{

    public function bar(\Illuminate\Database\DatabaseManager $db)
    {
        // $db->...
    }

}

There is no structural binding between db binding and Illuminate\Database\DatabaseManager interface internally, so I can bind anything to db key - is that correct? If I do that, will Laravel just crash? Looks like there are so many ways to access db binding:

dd(
    app('db'),
    app()->db,
    app()->make('db'),
    app(\Illuminate\Database\DatabaseManager::class),
    app()->make(\Illuminate\Database\DatabaseManager::class),
    app()['db'],
    app()[\Illuminate\Database\DatabaseManager::class]
);

How do you guys use container? Do you stick to interfaces or use/declare these simple key bindings? I'm still trying to understand the philosophy behind this.

0 likes
4 replies
martinbean's avatar

@RoboRobok The simple key bindings are just for convenience, and therefore a matter a preference.

Think of the container as a dictionary. You put something at a key, and you can reference that key to get its corresponding value. It just so happens, if your key is the name of an interface, you can type-hint it in a class and Laravel knows to fetch what’s set for that key.

Therefore, if you want to type-hint an interface and get a concrete implementation you can, but you can also bind to a simple key and access it using app($key).

I, personally, don’t set any values like this. I’ll bind classes to interfaces for dependency injection.

Robstar's avatar

I get most use out the container when a class has lots of dependencies to instantiate it:

$service = new SillyService(Adaptor $adaptor, Client $client, Resolver $resolver, Template $template);

Using that in multiple locations would be an issue.

Using the container I can use it via:

$service = resolve(SillyService::class);

as the container will resolve all dependencies for me. I tend to put things like this within it's own service provider for convenience.

RoboRobok's avatar

@Robstar is there any reason to avoid using container bindings inside your SillyService? That way you would still have them resolved and you wouldn't need to repeat injections.

Is it for transparency? To make dependencies explicit? And/or to make your service portable?

Please or to participate in this conversation.