Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Namespaces omitted for brevity...

I have written the following service provider and registered in config/app.php:

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

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

Here is my 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();
}

Now within the controller, I can inject the LossController as follows:

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

    }
}

However if I try to dependency inject the LossControlInterface from within a custom class called by a command:

[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

It appears as though I am unable to dependency inject the interface into a custom class, but I can when dependency injecting into a controller.

Any thoughts on what Im doing wrong or have omitted to get the automatic resolution working?

like image 264
Gravy Avatar asked Sep 02 '14 12:09

Gravy


1 Answers

The IoC is automatic within controllers, and you don't see the injection because Laravel handles the construction of controllers for you. When creating any other custom class by using the new keyword, you will still need to send in all of the parameters needed to it's constructor:

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

You can hide this, to a degree, by funneling creation of your custom class through a service provider:

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

Then just have the IoC make it whenever you need it:

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

In your case, you can change your code to look like this:

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

A perhaps cleaner approach could be to create a service provider and an OfferFactory which gets injected into your controller. The controller can then request the factory to create the offer whenever it needs one:

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

This has the benefit of completely decoupling your controller from the logic behind the creation of the offer, yet allowing you to have a spot to add any amount of complexity necessary to the process of creating offers.

like image 190
Jeff Lambert Avatar answered Oct 04 '22 16:10

Jeff Lambert