Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 4 One interface with multiple implementations at same time

Problem is that i don't know how to bind multitple implementations of one interface.

Example:

// One interface
interface SmsInterface {
...
}

// First implementation using SmsCoin
class SmscoinAPI implements SmsInterface {
...
}


// Second implementation using Fortumo
class FortumoAPI implements SmsInterface {
...
}

// Two controllers:

class SmsCoinController {
    public function __construct(SmsInterface $sms) {
        $this->sms = $sms
    }
}

class FortumoController {
    public function __construct(SmsInterface $sms) {
        $this->sms = $sms
    }
}
  1. Question: How I can bind SmsInterface with implementation FortumoApi for FortumoController, and bind SmsInterface with implementation SmsCoinApi for SmsCoinController ?

  2. I was using ServiceProvider for registering bindings, can I do it there ? If not where should bindings put?

EDIT:

I can't get answer anywhere, read many laravel books, everywhere is said to use multiple implementations, but nowhere is shown how to swap/switch those implementations.

If i have one interface and two implementations, how to bind and swap them in controller. Do i need to do that in that controller constructor method ? or in routes by checking controller's route or in filters.php ? or in Service provider ? and how to technically correclty write that code ?

like image 878
user991 Avatar asked Jan 07 '14 00:01

user991


2 Answers

This question took for me long time to solve it. After Laravel 5.0 is out i'm posting solution that i have found using Contextual binding

Consider this example, when one controller needs one interface with multiple implementations.

<?php

// We are creating one abstract controller with one logic
abstract class AbstractSearchController
{
    protected $searchService;

    public function __construct(SearchServiceInterface $searchService)
    {
        $this->searchService = $searchService;
    }

    public function getResult($keyword)
    {
        return $this->searchService->getItems($keyword);
    }
}

// In routes.php file we can point url like: http://example.com/search/posts/?keyword=my-search-keyword to use 
// something like this: Route::resource('search/posts', 'PostSearchController', ['only' => ['index']]);
class PostSearchController extends AbstractSearchController
{
    // No code here as this controller only is needed so that we can point Specific implementation to it
}

// In routes.php file we can point url like: http://example.com/search/members/?keyword=my-search-keyword to use 
// something like this: Route::resource('search/members', 'MemberSearchController', ['only' => ['index']]);
class MemberSearchController extends AbstractSearchController
{
    //
}

// Our main interface that will have multiple implementations at same time
interface SearchServiceInterface
{
    public function getItems($keyword);
}

// Our first implementation of interface
class MemberSearchRepo implements SearchServiceInterface
{
    public function getItems($keyword)
    {
        // Get members by keyword
    }
}

// Our second implementation of interface
class PostSearchRepo implements SearchServiceInterface
{
    public function getItems($keyword)
    {
        // Get posts by keyword
    }
}

// When PostsSearchController needs Search Logic point IoC to give our first implementation
$this->app->when('PostSearchController')->needs('SearchServiceInterface')->give('PostSearchRepo');

// When MemberSearchController needs Search Logic point IoC to give our seconds implementation
$this->app->when('MemberSearchController')->needs('SearchServiceInterface')->give('MemberSearchRepo');

I hope this extended example will help people to understand how to implement feature that I needed for Laravel 4.x with abillities that Laravel 5 provides

like image 56
user991 Avatar answered Nov 14 '22 23:11

user991


I've been trying to do the same, until I found it in the Laravel docs. It's called Contextual Binding, and the way in which you have to implement it is like this:

use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;

$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('local');
          });

$this->app->when(VideoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('s3');
          });

Hope it helps!

like image 22
facundofarias Avatar answered Nov 14 '22 23:11

facundofarias