0

For example I have a class named User and in its constructor I have a dependency named MyDependency. MyDependency does not have any dependencies itself so I don't need to bind it to he $app container.

When I instantiate the User class with no constructor parameters, where does laravel know that a constructer is triggered and it must resolve its dependencies?

I know that Service Container uses ReflectionClass to instantiate the Dependency. But there must be some function which gets triggered to perform this.

Abw
  • 1
  • 3

1 Answers1

1

It doesn't. You have to explicitly call App::make() to create an instance. The same goes for calling other methods using App:call(). Here's the relevant section of the docs: https://laravel.com/docs/10.x/container#the-make-method

UPD Assume the following controller:

<?php

namespace App\Http\Controllers;

use App\Models\Order;
use App\Services\OrderService;

class OrderController extends Controller
{
    protected OrderService $service;

    public function __construct(OrderService $service) {
        $this->service = $service;
    }

    public function update(Order $order)
    {
        return $this->service->update($order);
    }
}

The contents of OrderService:

<?php 

namespace App\Services;

use App\Models\Order;

class OrderService {
    public function __construct(bool $dry) { /* */ }
    public function update(Order $order) { /* */ }
}

In your service provider you have:

$this->app->when(OrderService::class)->needs('$dry')->give(false);

And in your routes/api.php

Route::get('/order/{order}', [OrderController::class, 'update']);

Now what happens when make a request to api/order/1? First, the OrderController instance is created. You provided the class name when registering the route, so Laravel knows which class to use. The instances is created with a call of make. As you mention in the quetion, reflections are used to determine, what arguments are expected by the constructor. In our case it's just the instance of OrderService. So now Laravel will try to resolve this dependency. It's a class, not an interface, so Laravel will simply try to create it's instance, again using make. Again, analysing the constructor it will determine that it needs boolean parameter $dry. For which we have registered a resolver in our service provider. So $dry gets passed to the OrderService constructor, Laravel gets an instance and passes it to OrderController constructor. Now it can call the update method. By the same logic, the call method is used to resolve all dependencies. In this case it's just model dependency, which Laravel can resolve out of the box.

The general idea here is that make and call are always being called somewhere under the hood. There's no mechanism to intercept a native call to constructor or method. For example, if you modify routes/api.php like this:

Route::get('/order/{order}', function($order) {
    $controller = new OrderController();
    return $controller->update($order);
});

You'll get an ArgumentCountError for the constructor call.

Alex Yokisama
  • 1,021
  • 7
  • 8
  • But laravel documentations says: If a class has no dependencies or only depends on other concrete classes (not interfaces), the container does not need to be instructed on how to resolve that class. https://laravel.com/docs/8.x/container#zero-configuration-resolution – Abw Apr 27 '23 at 16:00
  • @Abw This isn't exactly about your original question. If your class constructor expects a parameter and you try to create an instance of it using `new MyClass` you will get an Exception anyway. What the documentation says here is that if you call `make` method, laravel will first try to resolve dependencies automatically. – Alex Yokisama Apr 27 '23 at 16:19
  • For example I have a route which triggers createOrder function in my controller. This function has a PaymentOrder dependency. So when does `make` method get called so that laravel tries to resolve my dependency? I'm a little confused. – Abw Apr 28 '23 at 09:26
  • I've modified my answer and added an example. TLDR, the call happens somewhere under the hood, which is enabled by the fact, that you provide class and method names, when registering the route. I'd recommend you to experiment with using `make` and `call` vs calling methods directly. – Alex Yokisama Apr 29 '23 at 20:56