Laravel5 dependency injection on Model

Thanks to @svmm for referencing the question mentioned in the comments. I found that you cannot use dependency injection on Models because you would have to change the signature on the constructor which doesn't work with the Eloquent framework.

What I did as an intermediate step, while refactoring the code, is use App::make in the constructor to create the object, such as:

class Surface extends Model{
    public function __construct()
    {
        $this->zipCode = App::make('App\Repositories\ZipCodeRepositoryInterface');
    }

That way the IoC will still grab the implemented repository. I am only doing this until I can pull the functions into the repository to remove the dependency.


However it might not be a good practice to inject services into your models either by constructor or method injection, think about designing the system in such a way that you do not need to do that and instead maybe inject the model into a service.

Let's see an example(just a dummy example in order to get to the point!).

  • Say we have Basket and Order models, and we want to add orders to basket
  • And we have a discount service that calculates discount based on orders
  • Every time user adds an order to basket we need to calculate new discount and set it on basket

one approach is:

class OrderController
{
    function store(User $user, Order $order)
    {
        $basket = $user->getBasket();
        $basket->addOrder($order);
    }
}

class Basket
{
    private $discountService;

    public function __construct(DiscountService $discountService)
    {
        $this->discountService = $discountService;
    }

    function addOrder(Order $order)
    {
        $this->orders[] = $order;
        $discount = $this->discountService->calculateFor($this->orders);
        $this->discount = $discount;
    }
}

class DiscountService
{
    function calculateFor(array $orders) {
        // code for calculating discount;
        return $discount;
    }
}

In this approach we injected discount service into Basket model

Another better approach would be like this:

class OrderController
{
    private $discountService;

    public function __construct(DiscountService $discountService)
    {
        $this->discountService = $discountService;
    }

    function store(User $user, Order $order)
    {
        $basket = $user->getBasket();
        $basket->addOrder($order);
        $this->discountService->setDiscount($basket);
    }
}

class Basket
{
    function addOrder(Order $order)
    {
        $this->orders[] = $order;
    }

    function getOrders()
    {
        return $this->orders;
    }

    function setDiscount(int $discount)
    {
        $this->discount = $discount;
    }
}

class DiscountService
{
    function setDiscount(Basket $basket) {
        $discount = $this->calculateFor($basket->getOrders());
        $basket->setDiscount($discount);
    }

    private function calculateFor(array $orders)
    {
        // code for calculating discount
        return $discount;
    }
}
  • In the first approach basket is making the decision about having discount, but this is not basket's concern
  • In the first approach basket depends on discount service, but in real world you don't need a discount service to have a basket

In Laravel 5.7 you can use the global resolve(...) method. I don't think the global App is defined in more recent version of Laravel.

$myService = resolve(ServiceName::class);

Resolving in Laravel docs