starbolt
starbolt

Reputation: 467

Injecting Interface on class (Laravel Package)

I'm developing my own L5 package for handling payments. To be able to change the payment gateway in the future, I'm using interfaces.

My interface looks like this:

interface BillerInterface
{
    public function payCash();

    public function payCreditCard();
}

I also have a concrete implementation, which is the desired payment gateway.

class Paypal implements BillerInterface
{
    public function payCash()
    {
        // Logic
    }

    public function payCreditCard()
    {
        // Logic
    }
}

The Biller class is the main class, and the constructor method expects the above interface, like so:

class Biller {

    protected $gateway;

    public function __construct(BillerInterface $gateway)
    {

        $this->gateway = $gateway;
    }

    // Logic

}

Last, I created the service provider, to bind the interface to the gateway class.

public function register()
{
    $this->app->bind(BillerInterface::class, 'Vendor\Biller\Gateways\Paypal');
}

Seems to be working, but I'm getting an error when trying to instantiate the Biller class...

Biller::__construct() must be an instance of Vendor\Biller\Contracts\BillerInterface, none given

I tried the following code but doesn't seem to work...

public function register()
{
    $this->app->bind(BillerInterface::class, 'Vendor\Biller\Gateways\Paypal');

    $this->app->bind(Biller::class, function ($app) {
        return new Biller($app->make(BillerInterface::class));
    });
}

Any clues?

Upvotes: 2

Views: 1397

Answers (4)

Afraz Ahmad
Afraz Ahmad

Reputation: 5386

Talking about @MaGnetas answer

I prefer to bind class with interface using this way.

public function register()
{
    $this->app->bind(AppointmentInterface::class, AppointmentService::class);
}

This helps IDEs to find the path of the class and we can jump to that class by just clicking on it.

If we pass class path as string path like shown below:

public function register()
{
    $this->app->bind('App\Interfaces\AppointmentInterface', 'App\Services\AppointmentService');
}

Then IDE can not find the location of class when we click on this string.

Upvotes: 0

Martin Bean
Martin Bean

Reputation: 39389

You’re binding interfaces to an implementation fine in your service provider. But dependencies will only be resolved by the service container, i.e.

class SomeClass
{
    public function __construct(Billing $billing)
    {
        $this->billing = $billing;
    }
}

Laravel’s service container will read the type-hint of the constructor method’s parameters, and resolve that instance (and also any of its dependencies).

You won’t be able to “new up” the Billing instance directly (i.e. $billing = new Billing) because the constructor is expecting something implementing BillingInterface, which you’re not providing.

Upvotes: 2

Vucko
Vucko

Reputation: 7479

This is how I've done it in my app and it seems to be working:

public function register()
{
    $this->app->bind('DesiredInterface', function ($app) {
        return new DesiredImplementationClass(
            $app['em'],
            new ClassMetaData(DesiredClass::class)
        );
    });
}

Upvotes: 0

MaGnetas
MaGnetas

Reputation: 5108

When you're binding interface to actual class try replacing the BillerInterface::class with a string '\Your\Namespace\BillerInterface'

Upvotes: 0

Related Questions