Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel5 dependency injection on Model

I have an Eloquent Model called Surface which is dependent on a ZipCodeRepository object:

class Surface extends Model{
    public function __construct(ZipCodeRepositoryInterface $zipCode){...}

and an Address object that hasMany surfaces.

class Address extends Model{
    public surfaces() { return $this->hasMany('App/Surface'); }
}

My issue is when I call $address->surfaces I get the following error:

Argument 1 passed to App\Surface::__construct() must be an instance of App\Repositories\ZipCodeRepositoryInterface, none given

I thought the IoC would automatically inject that.

like image 256
ajon Avatar asked Nov 10 '15 18:11

ajon


People also ask

How do I perform dependency injection in Laravel?

In Laravel, dependency injection is the process of injecting class dependencies into a class through a constructor or setter method. This allows your code to look clean and run faster. Dependency injection involves the use of a Laravel service container, a container that is used to manage class dependencies.

Does Laravel have dependency injection?

The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

What is singleton in Laravel?

The singleton pattern is used to restrict the instantiation of a class to a single object, which can be useful when only one object is required across the system. … The first call will instantiate the object while any subsequent will only return the instantiated object.

What is IOC container in Laravel?

The Laravel inversion of control container is a powerful tool for managing class dependencies. Dependency injection is a method of removing hard-coded class dependencies. Instead, the dependencies are injected at run-time, allowing for greater flexibility as dependency implementations may be swapped easily.


2 Answers

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
like image 67
Kamal Behboudi Avatar answered Sep 21 '22 21:09

Kamal Behboudi


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

like image 21
Michael Rivera Avatar answered Sep 23 '22 21:09

Michael Rivera