我如何在Laravel中创建一个控制器构造函数,它接受同一接口的两个具体实现? 背景

我如何在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) {

注意:这是使用Laravel 5.3,请不要判断

我们正在尝试对我们的laravel控制器使用依赖注入,并将尽可能多的业务逻辑推送到REPO中,这些REPO在控制器实例化时被注入控制器

我们已经有了这样一个运行示例:

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

    }
}