Should Laravel’s App::make() be considered a dependency?

I think the question in general is best summed up as, should Laravel’s App::make() be considered a dependency? On the one hand it can instantiate any number of different implementations, so maybe it is not a hard dependency. On the other hand, should a class really be responsible for calling App::make() to instantiate its own dependencies, or would it be better to have them be passed as parameters to the class? What is the cleanest/most SOLID approach?

Here is a specific example:

An app makes an API request to an external server. One class handles making the actual http request which will implement HttpClientInterface and one class makes the request and fetches/parses the data, call it LookUp. Finally, a service class is ultimately responsible for calling the lookUp()
method on LookUp class.

I am trying to think of the best way to do this.

Option 1:

Instantiate dependencies in the service class and pass them through as parameters:

class LookUpService
{
    public function lookUp($identifier)
    {
        $client = App::make(HttpClientInterface::class);
        $provider = new LookUp($client);

        $results = $provider->lookUp($identifier);
    }
}

Option 2:

Alternatively, since we are anyway using Laravel’s App::make(), we could hide that within the LookUp class.

class LookUpService
{
     public function lookUp($identifier)
     {
         $provider = new LookUp();
         $results = $provider->lookUp($identifier);
     }
}

class LookUp
{
     public function __construct()
     {
         $this->client = App::make(HttpClientInterface::class);
     }
}

Which is better?

You should be looking at making the provider implementing an interface that is passed in to Service’s constructor. Your Service should have a private property that will be the Interface and in the constructor you assign your argument to the property. This way you can later mock your class and do unit testing. I don’t know PHP that well so I am just going to write some mimicked code here:

class LookUpService implements LookUpServiceInterface
{
    private ProviderInterface $_provider
    public function __constructor(ProviderInterface $provider)
    {
        $_provider = provider
    }
    public function lookUp($identifier)
    {
        $results = $_provider->lookUp($identifier);
    }
}

class LookUp implements ProviderInterface
{
    private HttpClientInterface _client
     public function __construct(HttpClientInterface client)
     {
         $_client = client;
     }
}

class Controller
{
    private LookUpServiceInterface _lookUpService;
    public function __construct()
    {
        _lookUpService = new LookUpService(new LookUp(App::make(HttpClientInterface::class)));
    }

    public function someFunction()
    {
        _lookUpService.lookUp("identifier")
    }
}

Also, you should do the same thing to the LookUp class. It should take HttpClientInterface in constructor.

You instantiate all those classes in controller (or wherever you call the service from). This makes sure your classes are not dependent on each other and can be mocked using interfaces and unit tested.

Option 1:
If you do it this way, you can’t mock your service because it will always call LookUp() and App:make().

Option 2:
Pretty much same issue as in Option 1. You want to make sure that your service, repositories and so one use Interfaces so that you can easily mock them or change the implementing class level above the Service.

6

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *