Laravel-ioc自动解析-从控制器工作,但不能从自定义类工作


Laravel ioc automatic resolution - works from controller but not from custom class

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

我已经编写了以下服务提供商,并在config/app.php中注册:

class OfferServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->registerLossControlManager();
    }
    protected function registerLossControlManager()
    {
        $this->app->bind('LossControlInterface', 'LossControl');
    }
}

这是我的LosControlInterface

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

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

有没有想过我做错了什么,或者为了让自动解决方案发挥作用而遗漏了什么?

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

这样做的好处是,您的控制器与创建优惠背后的逻辑完全脱钩,同时允许您在创建优惠的过程中增加任何必要的复杂性。

在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]);

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

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

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

相关文章: