0

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?

asked Mar 25, 2016 at 13:45

1 Answer 1

1

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.

answered Mar 25, 2016 at 14:11
6
  • 1
    I appreciate your answer. I guess my follow up question would be, you used App::make in the constructor for the LookUp class, but then in the LookUpService you are asking for the implementation as a parameter. Why not use App::make here too? Basically when would it be appropriate to use and when not Commented Mar 25, 2016 at 14:49
  • @tam I edited the code. Controller class is what would be calling the service and it would instantiate all objects. Commented Mar 25, 2016 at 17:46
  • I hear your point about not being able to mock the service, though you just remove it one step further back to the controller. I was just wrapping that in the service for clarity. the service doesn't do anything else. The other issue i have is that you still seem to be inconsistent, unless I am missing something. You are calling App::make on the HttpClientInterface, but explicitly instantiating the LookUpServiceInterface and ProviderInterface. Why not call App::make for all of them? And once it is App::make and not an explicit instantiation, where is really the best place for it? Commented Mar 25, 2016 at 18:11
  • You can call App:make for everything in the most outward class. What I did is manually instantiating the class which is the same as what App:make does automatically. If your service doesn't do anything then why is it there? The role of the service is to communicate between controller and domain access (repository for example). Your service should take the data from Provider do whatever business logic it has to do, map it to a DTO and return to controller. Commented Mar 25, 2016 at 19:07
  • you're right about services, what I was doing in this example doesn't make much sense, in my actual case it is performing some mappings. I guess I still just am not understanding when and where to use App::make? Commented Mar 25, 2016 at 21:26

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.