2014-01-07 41 views
4

問題是我不知道如何綁定一個接口的多個實現。Laravel 4同時有多個實現的一個接口

例子:

// One interface 
interface SmsInterface { 
... 
} 

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


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

//兩個控制器:

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

class FortumoController { 
    public function __construct(SmsInterface $sms) { 
     $this->sms = $sms 
    } 
} 
  1. 問:如何我可以執行FortumoApi結合SmsInterface爲FortumoController,並與實施SmsCoinApi爲SmsCoinController結合SmsInterface?

  2. 我正在使用ServiceProvider註冊綁定,我可以在那裏做到嗎?如果不是應該在哪裏綁定?

編輯:

我不能回答任何地方,看了很多書laravel,到處是說使用多種實現,但是無處展示瞭如何交換/切換的實現。

如果我有一個接口和兩個實現,如何在控制器中綁定和交換它們。我是否需要在該控制器構造函數方法中執行該操作?或在路由中檢查控制器的路由或在filters.php?或在服務提供商?以及如何在技術上正確地編寫該代碼?

+1

我碰到這個問題。 – user991

回答

4

這個問題花了很長時間才解決。在Laravel 5.0發佈後,我發佈了使用上下文綁定的解決方案

考慮這個例子,當一個控制器需要一個接口與多個實現。

<?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'); 

我希望這個擴展的例子將幫助人們理解如何實現的功能,我需要Laravel 4.x版與Laravel 5提供

+1

我也在尋找答案,fyi,laravel 5支持上下文綁定只是爲了做這種事情.. – slier

0

通過將接口綁定到IOC中的類(從您的服務提供者中),您正在「全局」執行此綁定,因此您將無法使用IOC自動注入不同的類。

您可以使用從路由關閉中注入的正確類來手動返回控制器實例。

Route::get('sms', function() 
{ 
    return new SmsCoinController(new SmscoinAPI); 
}); 

Route::get('fortumo', function() 
{ 
    return new FortumoController(new FortumoAPI); 
}); 

要麼在構造函數中使用接口提示,要麼根本不使用接口提示。只要提示實際的課程,國際奧委會會爲你注入。

1

雖然你的答案工程abillities,我已經運行到同樣的問題在我自己的項目中。我想出的解決方案是在執行之前決定要使用哪個特定接口的實現甚至到達控制器。

許多人使用global.php文件爲具體類設置綁定。這很好,我嘗試了很長時間,但是你的應用程序越大,你將開始製作的綁定越多,而當文件真的意味着要捕獲所有文件時,文件會變得有點長。我開始將所有綁定存儲在bindings.php文件的start文件夾中,並在global.php文件的末尾包含require_once

無論如何,在我的bindings.php文件中,我放置了所有的邏輯來確定此服務器請求需要哪些具體實現。在我的情況下,我的具體類由用戶請求的縣決定,並通過POST或GET變量指定。

if(Input::has('state') && Input::has('county')) 
{ 
    $var_name = strtoupper(Input::get('state')).'.'.ucfirst(strtolower(Input::get('county'))); 
    App::bind('SearchModelInterface', Config::get('counties.'.$var_name.'.searchClass')); 
    App::bind('InserterModelInterface', Config::get('counties.'.$var_name.'.inserterClass')); 
    Config::set('propertyClassName', Config::get('counties.'.$var_name.'.propertyClass')); 
    Config::set('chosenCounty', strtolower(Input::get('county'))); 

    App::bind('Property', function($app) 
    { 
     $class = Config::get('propertyClassName'); 
     return new $class(); 
    }); 

}else{ 
    App::bind('SearchModelInterface', 'Harris_TX_SearchModel'); 
    App::bind('InserterModelInterface', 'Harris_TX_InserterModel'); 
    App::bind('Property', function($app) 
    { 
     return new Harris_TX_Property(); 
    }); 
    Config::set('chosenCounty', 'harris'); 
} 

正如你所看到的,還有的是根據請求在其上縣根據配置文件中設置若干個接口,並提供了默認值,如果沒有的。看到Laravel的所有Facades在這裏都是完全可用的,其中包括Config facade,這使我可以設置將在整個請求執行過程中使用的值。

我從來沒有見過像泰勒,傑弗裏或社區其他任何大人物推薦的東西,但這對我來說真的很好。

1

我一直在嘗試做同樣的事情,直到我在Laravel文檔中找到它。這就是所謂的Contextual Binding,並在其中你必須實現它是這樣的方式:

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'); 
      }); 

希望它能幫助!