我如何在Laravel中创建一个控制器构造函数,它接受同一接口的两个具体实现? 背景
注意:这是使用Laravel 5.3,请不要判断 我们正在尝试对我们的laravel控制器使用依赖注入,并将尽可能多的业务逻辑推送到REPO中,这些REPO在控制器实例化时被注入控制器 我们已经有了这样一个运行示例:我如何在Laravel中创建一个控制器构造函数,它接受同一接口的两个具体实现? 背景,laravel,laravel-5,dependency-injection,Laravel,Laravel 5,Dependency Injection,注意:这是使用Laravel 5.3,请不要判断 我们正在尝试对我们的laravel控制器使用依赖注入,并将尽可能多的业务逻辑推送到REPO中,这些REPO在控制器实例化时被注入控制器 我们已经有了这样一个运行示例: class AcmeController extends Controller { protected $repository; public function __construct(AcmeInterface $repository) {
class AcmeController extends Controller
{
protected $repository;
public function __construct(AcmeInterface $repository)
{
$this->repository = $repository;
}
}
在app/Providers/RepositoryServiceProvider.php中,我们进行绑定:
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
$this->app->bind(\App\Repositories\Contracts\AcmeInterface::class, \App\Repositories\OpCity\AcmeRepo::class);
}
}
然后AcmeRepo自然地实现AcmeInterface:
class AcmeRepo implements AcmeInterface
问题:
现在我们有一个例子,相同模型的一些数据被持久化在内存类型存储(redis)中,其余的数据被持久化在关系数据库存储(psql)中。我们希望有两个独立的回购协议,其中每个回购协议都特定于其存储类型,即RedisAcmeRepo和SqlAcmeRepo
如何在AcmeController
构造函数中执行此操作
public function __construct(AcmeInterface $sqlRepo, AcmeInterface $redisRepo)
{
$this->sqlRepo = $sqlRepo;
$this->redisRepo = $redisRepo;
}
例如,您可以执行以下操作:
$this->app->bind(AcmeController::class, function ($app) {
return new AcmeController($app->make(sqlRepo::class), $app->make(redisRepo::class));
});
或者这个:
$this->app->when(AcmeController::class)
->needs('$sqlRepo')
->give($app->make(sqlRepo::class));
$this->app->when(AcmeController::class)
->needs('$redisRepo')
->give($app->make(redisRepo::class));
基于以上答案,我提出了这个解决方案,这种解决方案也使用了复合模式(我将回购协议的名称从Acme更改为ShopperLogs):
RepositoryServiceProvider::register()我们如何知道控制器需要repo?需要的标准是什么?所以我看了一下文件,看到了。。我明白这个什么时候/needs/give
语法意味着什么。然而,文档中的示例使用了类实例ie->needs(Filesystem::class)
,在您的例子中,您依赖于一个文本:即如果有人调用该类并使用变量'$sqlRepo'
。。有趣的是,我看到一些关于文本绑定的非官方文档,我认为您设计解决方案的方法是错误的——如果模型存储在一个repo中会发生什么?您将重写控制器代码的一部分,它将被注入一个无用的repo实例。您应该向控制器注入一些ModelMaintainer
类的实例,这些类将处理所有持久化的模型内容。然后,ModelMaintainer
的特定实现将有不同存储库的特定注入。控制器应该只在$this->modelmainator->store($model)
中使用抽象,而不知道该模型的各个部分是如何存储的。@d3jn所以我们这里有3层:1。控制员2。模型维护者3。处理持久化的回购。我从没听说过“模型维护者”这个词。。那是什么设计模式?此外,modelMaintainer中会有什么样的代码?ie也会有业务逻辑吗?modelmaintener
只是一个名字。您可以将其视为策略
模式-您有一项任务是在内存中持久化模型。它背后的细节应该在单独的类中实现。然后,当逻辑发生变化时,您将只改变这个单独类的实现并交换策略。在当前版本中,您正在更改控制器构造函数的签名。谢谢@d3jn,您的评论启发了我的答案
<?php
interface ShopperLogInterface
{
public function getLogs($from, $to, $shopper);
}
class ShopperLogsController extends Controller
{
/**
* service
*
* @var \App\Repositories\Contracts\ShopperLogInterface
* @access protected
*/
protected $manager;
public function __construct(ShopperLogInterface $manager)
{
$this->manager = $manager;
}
}
class ShopperLogManager implements ShopperLogInterface
{
protected $sqlRepo;
protected $redisRepo;
public function __construct(ShopperLogInterface $sqlRepo, ShopperLogInterface $redisRepo)
{
$this->sqlRepo = $sqlRepo;
$this->redisRepo = $redisRepo;
}
public function getLogs($from, $to, $shopper)
{
$todayRange = //get the today part of from -- to
/**
* array of ShopperLogs
*/
$todaysLogs;
if ($todayRange) {
$this->redisRepo->getLogs($todayRange->start, $todayRange->finish, $shopper);
}
$legacyRange = //get the part of from -- to that excludes today's range
/**
* array of ShopperLogs
*/
$legacyLogs;
if ($legacyLogs) {
$this->sqlRepo->getLogs($todayRange->start, $todayRange->finish, $shopper);
}
return merge($todayRange, $legacyRange);
}
}
class ShopperLogsSqlRepo implements ShopperLogInterface
{
/**
* @var /Illuminate\Database\Eloquent\Model/ShopperLogs
*/
protected $model;
/**
* @param /Illuminate\Database\Eloquent\Model/ShopperLogs $model
*/
public function __construct(ShopperLogs $model)
{
$this->model = $model;
}
public function getLogs($from, $to, $shopper)
{
$this->model->whereLogs //do eloquent sql stuff here
}
}
class ShopperLogsRedisRepo implements ShopperLogInterface
{
/**
* @var \Redis\Model\Class
*/
protected $model;
/**
* @param \Redis\Model\Class $model
*/
public function __construct(ShopperLogs $model)
{
$this->model = $model;
}
public function getLogs($from, $to, $shopper)
{
$this->model->whereLogs //do redis stuff
}
}
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
$this->app->bind(\App\Repositories\Contracts\ShopperLogInterface::class, \App\Managers\ShopperLogManager::class);
$this->app->bind(ShopperLogsController::class, function ($app) {
return new ShopperLogsController($app->make(ShopperLogManager::class));
});
$this->app->bind(\App\Repositories\Contracts\ShopperLogInterface::class, function() {
return new \App\Managers\ShopperLogManager(new \App\Repositories\ShopperLogsSqlRepo(new \App\ShopperLog), new \App\Repositories\ShopperLogsRedisRepo(new \App\ShopperLog));
});
}
}