Laravel ioc自动分辨率 - 从控制器工作,但不从自定义类工作

时间:2014-09-02 12:37:37

标签: php laravel repository-pattern ioc-container

为简洁起见省略了命名空间......

我编写了以下服务提供程序并在config / app.php中注册:

class OfferServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->registerLossControlManager();
    }

    protected function registerLossControlManager()
    {
        $this->app->bind('LossControlInterface', 'LossControl');
    }
}

这是我的LossControlInterface

interface LossControlInterface
{
    /**
     * @param int $demandId
     * @param float $offerTotal
     * @param float $productTotal
     * @param null|int $partnerId
     * @return mixed
     */
    public function make($demandId, $offerTotal, $productTotal, $partnerId = null);

    /**
     * @return float
     */
    public function getAcceptableLoss();

    /**
     * @return bool
     */
    public function isAcceptable();

    /**
     * @return bool
     */
    public function isUnacceptable();

    /**
     * @return null
     */
    public function reject();
}

现在在控制器中,我可以按如下方式注入LossController:

use LossControlInterface as LossControl;

class HomeController extends BaseController {
   public function __construct(LossControl $lossControl)
    {
        $this->lossControl = $lossControl;
    }

    public function getLossThresholds()
    {
        $lossControl = $this->lossControl->make(985, 1000, null);

        var_dump('Acceptable Loss: ' . $lossControl->getAcceptableLoss());
        var_dump('Actual Loss: ' . $lossControl->calculateLoss());
        var_dump('Acceptable? ' . $lossControl->isAcceptable());

    }
}

但是,如果我尝试依赖从命令调用的自定义类中注入LossControlInterface:

[2014-09-02 13:09:52] development.ERROR: exception 'ErrorException' with message 'Argument 11 passed to Offer::__construct() must be an instance of LossControlInterface, none given, called in /home/vagrant/Code/.../ProcessOffer.php on line 44 and defined' in /home/vagrant/Code/.../Offer.php:79

好像我无法将接口注入自定义类,但是当依赖注入控制器时,我可以。

对于我做错了什么或省略了让自动解决方案有效的任何想法?

3 个答案:

答案 0 :(得分:9)

IoC在控制器内是自动的,你不会看到注射,因为Laravel会为你处理控制器的构造。使用new关键字创建任何其他自定义类时,您仍需要发送其所需的所有参数:

$myClass = new ClassWithDependency( app()->make('Dependency') );

您可以通过服务提供商汇集创建自定义类来在一定程度上隐藏此内容:

// Your service provider
public function register()
{
    $this->app->bind('ClassWithDependency', function($app) {
        return new ClassWithDependency( $app->make('Dependency') );
    });
}

然后让IoC在您需要的时候制作它:

$myClass = app()->make('ClassWithDepenency');

在您的情况下,您可以将代码更改为:

private function setOffer(Offer $offer = null) {
    $this->processOffer    = $offer ?: 
        new Offer( app()->make('LossControlInterface') );
}

一种可能更简洁的方法可能是创建一个服务提供者,并将OfferFactory注入您的控制器。然后,控制器可以在需要时请求工厂创建报价:

// Controller
public function __construct(OfferFactory $offerFactory)
{
    $this->offerFactory = $offerFactory;
}

public function setOffer(Offer $offer = null)
{
    $this->processOffer = $offer ?: $this->offerFactory->createOffer();
}

// OfferFactory
class OfferFactory
{
    public function createOffer()
    {
        return app()->make('Offer');
    }
}

这样做的好处是可以将控制器与创建商品背后的逻辑完全分离,同时允许您在创建商品的过程中添加任何必要的复杂性。

答案 1 :(得分:1)

在Laravel 5.2中,针对您的特定问题的最简单的解决方案是替换

new Offer();

App::make('Offer');

甚至更短

app('Offer');

将使用Laravel Container来处理依赖关系。

但是,如果您想将其他参数传递给Offer构造函数,则必须在服务提供者中将其绑定

App::bind('Offer', function($app, $args) {
    return new Offer($app->make('LossControl'), $args);
});

瞧,现在你可以写了

app('Offer', [123, 456]);

答案 2 :(得分:0)

在laravel 5.4(https://github.com/laravel/framework/pull/18271)中,您需要使用IoC容器的新makeWith方法。

App::makeWith( 'App\MyNameSpace\MyClass', [ $id ] );

如果您仍然使用5.3或更低版本,上述答案将有效。