问题是我不知道如何绑定一个接口的多个实现。
示例:
// 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
}
}
问题:我如何使用FortumoController的实现FortumoApi绑定SmsInterface,并将SmsInterface与SmsCoinController的实现SmsCoinApi绑定?
我使用ServiceProvider注册绑定,我可以在那里做吗?如果没有绑定的位置?
修改
我无法在任何地方得到答案,阅读了许多laravel书籍,据说在任何地方使用多个实现,但没有显示如何交换/切换这些实现。
如果我有一个接口和两个实现,如何在控制器中绑定和交换它们。我是否需要在该控制器构造函数方法中执行此操作?或通过检查控制器的路线或在filters.php的路线?还是在服务提供商?以及如何在技术上相关地编写该代码?
答案 0 :(得分:6)
这个问题花了我很长时间来解决它。在Laravel 5.0出局后,我发布了使用Contextual binding
发现的解决方案考虑这个例子,当一个控制器需要一个具有多个实现的接口时。
<?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所需的功能以及Laravel 5提供的功能
答案 1 :(得分:2)
我一直在努力做同样的事情,直到我在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');
});
希望它有所帮助!
答案 2 :(得分:1)
虽然你的答案有效,但我在自己的项目中遇到了同样的问题。我想出的解决方案是决定在执行之前使用哪个特定接口的实现甚至到达控制器。
许多人使用global.php
文件为具体类设置绑定。这很好,我试了很长时间,但你的应用程序越大,你开始制作的绑定就越多,而且当文件真正意义上是一个全部的时候它会变得有点长。我开始将所有绑定存储在bindings.php
文件夹中的start
文件夹中,并在require_once
文件的末尾包含global.php
。
无论如何,在我的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的所有外观在这里都是完全可用的,包括Config外观也很有帮助,它允许我设置将在整个请求执行过程中使用的值。
我从未见过Taylor,Jeffrey或社区中任何其他知名人士推荐过的这类内容,但这对我来说非常有用。
答案 3 :(得分:0)
通过将接口绑定到IOC中的类(来自服务提供者),您将“全局”执行此绑定,因此您将无法使用IOC自动注入不同的类。
您可以使用从路由闭包中注入的正确类来手动返回控制器的实例。
Route::get('sms', function()
{
return new SmsCoinController(new SmscoinAPI);
});
Route::get('fortumo', function()
{
return new FortumoController(new FortumoAPI);
});
或者根本不使用构造函数中的接口提示。只是提示实际的班级,IOC会为你注入它。