Reputation: 8968
When using Dependency Injection should dependencies be passed into a constructor individually, or is it acceptable to pass the entire DI container?
For example ... I have a repository called 'UserRepository'. It contains the following methods:
<?php
namespace MyApp\Repositories;
use \MyApp\Models\User;
class UserRepository {
private $ci;
public function __construct($ci)
{
$this->ci = $ci;
}
public function hashPassword($password)
{
return password_hash($password, PASSWORD_BCRYPT, [
'cost' => 15
]);
}
public function create($firstname, $lastname, $email, $password)
{
$user = User::create([
'firstname' => $firstname,
'lastname' => $lastname,
'email' => $email,
'password' => $this->hashPassword($password)
]);
return $user;
}
public function activateUser($userID)
{
$user = User($userID);
$user->email_verified = 1;
$user->save();
$verification = $user->verification();
$verification->is_used = 1;
$verification->validated_at = $this->ci->get('Carbon')::now();
$verification->save();
}
}
The Carbon
dependancy is available because I've passed in the Pimple
container. I can access any dependency in this way (so long as they're registered).
I'm using Slim3
which promotes this sort of DI. But, in applications like Laravel
I see dependencies being passed into the constructor individually.
Any advice?
Upvotes: 0
Views: 237
Reputation: 36989
When you pass the Dependency Injection Containers to a class, you call it "Service Locator". With a Service Locator your class is still responsible to instantiate its dependencies, and as such you should also unit test it. But how? Your object can't exist without a service locator, and test it isn't easy. If you pass the dependency into the constructor you can just mock them.
In your class you have this:
$verification->validated_at = $this->ci->get('Carbon')::now();
where Carbon
is the service name. Now you should keep in mind that the Service Locator that you inject to the class require a service with that name, and it should returns an instance of the Carbon\Carbon
class. What if your Service Locator has a missing Carbon
service or what if it returns a whole different object? You should test it with something like this to be sure not to break anything:
$this->assertInstanceOf(Carbon\Carbon::class, $container->get('Carbon'));
and more important, if you want reuse your object somewhere else you need to implement a specific service locator implementation.
By using a DIC your object isn't responsible to instantiate its dependencies anymore:
$container['user.repository'] = function ($c) {
return new UserRepository($c['Carbon']);
};
Your class is more reusable and writing tests is more easier.
Upvotes: 2