Maruu's avatar
Level 38

Multiple Bindings Example in Official Documentation

Hi everyone,

I was reviewing the official Laravel documentation and came across the following example in the AppServiceProvider:

namespace App\Providers;

use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\ServerToolsProvider;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * All of the container bindings that should be registered.
     *
     * @var array
     */
    public $bindings = [
        ServerProvider::class => DigitalOceanServerProvider::class,
    ];

    /**
     * All of the container singletons that should be registered.
     *
     * @var array
     */
    public $singletons = [
        DowntimeNotifier::class => PingdomDowntimeNotifier::class,
        ServerProvider::class => ServerToolsProvider::class,
    ];
}

In this example, it appears that the ServerProvider interface is being bound to two different concrete classes: DigitalOceanServerProvider in the $bindings array and ServerToolsProvider in the $singletons array.

However, with this setup, it seems that only one of these concrete classes will be resolved by the container, which doesn’t appear to be correct. Is this a mistake in the documentation, or is there another intention behind structuring the example this way?

0 likes
1 reply
LaryAI's avatar
Level 58

The example you provided from the Laravel documentation does indeed show the ServerProvider interface being bound to two different concrete classes: DigitalOceanServerProvider in the $bindings array and ServerToolsProvider in the $singletons array. This can be confusing because it seems to suggest that the same interface is being bound to two different implementations, which is not possible in a straightforward manner.

In Laravel, when you bind an interface to a concrete class, the last binding will overwrite any previous bindings. Therefore, in your example, the ServerProvider interface will ultimately be bound to ServerToolsProvider as a singleton, and the binding to DigitalOceanServerProvider will be ignored.

Here’s a corrected version of the AppServiceProvider that avoids this confusion:

namespace App\Providers;

use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * All of the container bindings that should be registered.
     *
     * @var array
     */
    public $bindings = [
        // Bind ServerProvider to DigitalOceanServerProvider
        ServerProvider::class => DigitalOceanServerProvider::class,
    ];

    /**
     * All of the container singletons that should be registered.
     *
     * @var array
     */
    public $singletons = [
        // Bind DowntimeNotifier to PingdomDowntimeNotifier
        DowntimeNotifier::class => PingdomDowntimeNotifier::class,
    ];
}

In this corrected version, the ServerProvider interface is only bound to DigitalOceanServerProvider in the $bindings array, and the DowntimeNotifier interface is bound to PingdomDowntimeNotifier in the $singletons array. This ensures that each interface is bound to a single concrete class, avoiding any conflicts.

If you need to bind multiple implementations to the same interface, you can use tags or contextual binding. Here’s an example using contextual binding:

namespace App\Providers;

use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\ServerToolsProvider;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        // Bind ServerProvider to DigitalOceanServerProvider
        $this->app->bind(ServerProvider::class, DigitalOceanServerProvider::class);

        // Bind DowntimeNotifier to PingdomDowntimeNotifier
        $this->app->singleton(DowntimeNotifier::class, PingdomDowntimeNotifier::class);

        // Contextual binding example
        $this->app->when(SomeSpecificClass::class)
                  ->needs(ServerProvider::class)
                  ->give(ServerToolsProvider::class);
    }
}

In this example, ServerProvider is generally bound to DigitalOceanServerProvider, but when SomeSpecificClass needs ServerProvider, it will be given ServerToolsProvider instead. This allows for more flexible and context-specific bindings.

Please or to participate in this conversation.