Understanding my usage of interfaces / contracts in Laravel

Viewed 34

trying to increase my PHP / Laravel knowledge so whilst creating a new feature I've tried to work with an Interface.

To set the scenario: Our company has changes direct debit providers a few times in the space of a year, I'm looking to creating this interface to make any future changes more 'scaffolded'

My code structure:

App\Interfaces\DirectDebitInterface

interface DirectDebitInterface {
    public function createAccount($account);
    // additional methods
}

App\Services\DirectDebit\Clients\Bottomline

class Bottomline implements DirectDebitInterface {
    public function getAccount(String $reference)
    {
        // do external API call, return back data
    }
}

App\Providers\AppServiceProvider @register

$this->app->bind(
    DirectDebitInterface::class,
    config('services.direct_debit_clients.' . // Bottomline
    config('services.direct_debit_clients.default') . '.class') // App\Services\DirectDebit\Clients\Bottomline::class
);

My current usage works but feels incorrect, here's a test endpoint using the getAccount() method:

public function getAccount(DirectDebitInterface $directDebitInterface)
{
    dd($directDebitInterface->getAccount('OS10129676'));
}

My first smell is that I've never seen someone use interface in the variable set for the class?

My second smell is that I'm using Livewire to load in the data and can't get my head around how to use the interface.

Here's my example code for the second smell:

App\Http\Livewire\Example

public function mount(Account $account)
{
    self::getDirectDebitAccount();
}

private function getDirectDebitAccount(DirectDebitInterface $directDebitInterface)
{
    dd($directDebitInterface->getAccount($reference));
}

The above fails because the method expects a param to be passed in, but I also can't instantiate the class as it's an interface.

Apart from feeling like there are some fundamental gaps in my knowledge... it seems I'm on the right track but my usage of the class/interface isn't setup right.

Any suggestions on how I should be calling this interface from within methods or where I've gone wrong with something?

Ta,

1 Answers

What you're doing is performing method-based dependency injection which is perfectly valid but arguably less common than constructor-based dependency injection. Both have a similar outcome, injecting a required dependency, with the main difference being the scope of the dependency (method vs. class).

In a standard Laravel/PHP environment that constructor injection might look as follows:

private DirectDebitInterface $directDebitor;

public function __construct(DirectDebitInterface $directDebitor)
{
    $this->directDebitor= $directDebitor;
}

public function doSomething(string $account)
{
    dd($this->directDebitor->getAccount($account));
}

Livewire is slightly different as you don't make use of the __construct function in a Component instead, you need to use the mount() function.

public function mount(DirectDebitInterface $directDebitor)
{
    $this->directDebitor = $directDebitor;
}

Assuming you've configured your bindings in the Service Container correctly, everything should work.

Related