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

时间:2018-07-19 15:04:20

标签: laravel laravel-5 dependency-injection

背景

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

我们正在尝试将依赖注入与laravel控制器一起使用,并将尽可能多的业务逻辑推入存储库中,这些存储库在控制器实例化时注入到控制器中。

我们已经有一个可以运行的示例:

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

问题

现在,我们有一种情况,其中 same 模型的某些数据保留在内存类型存储(redis)中,其余数据保留在关系数据库存储(psql)中。我们希望有两个单独的存储库,其中每个存储库都特定于其存储类型,即RedisAcmeRepo和SqlAcmeRepo

如何在AcmeController构造函数中做到这一点?

public function __construct(AcmeInterface $sqlRepo, AcmeInterface $redisRepo)
{
    $this->sqlRepo = $sqlRepo;
    $this->redisRepo = $redisRepo;
}

2 个答案:

答案 0 :(得分:1)

例如,您可以这样做:

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

答案 1 :(得分:1)

基于上述答案,我想出了这种解决方案,也使用了复合模式(我将存储库的名称从Acme更改为ShopperLogs):

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

    }
}