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.