Passing Dependencies
Arguments, or “dependencies” in DI terminology, can be passed to classes in the following main ways:
- passing by constructor
- passing by method (called a setter)
- by setting a property
- by method, annotation or attribute inject
We will now illustrate the different variants with concrete examples.
Constructor Injection
Dependencies are passed as arguments to the constructor when the object is created:
class MyClass
{
/** @var Cache */
private $cache;
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
$obj = new MyClass($cache);
This form is useful for mandatory dependencies that the class absolutely needs to function, as without them the instance cannot be created.
Setter Injection
Dependencies are passed by calling a method that stores them in a private properties. The usual naming convention for these
methods is of the form set*()
, which is why they are called setters, but of course they can be called
anything else.
class MyClass
{
/** @var Cache */
private $cache;
public function setCache(Cache $cache)
{
$this->cache = $cache;
}
}
$obj = new MyClass;
$obj->setCache($cache);
This method is useful for optional dependencies that are not necessary for the class function, since it is not guaranteed that the object will actually receive them (i.e., that the user will call the method).
At the same time, this method allows the setter to be called repeatedly to change the dependency. If this is not desirable, add a check to the method.
class MyClass
{
/** @var Cache */
private $cache;
public function setCache(Cache $cache)
{
if ($this->cache) {
throw new RuntimeException('The dependency has already been set');
}
$this->cache = $cache;
}
}
The setter call is defined in the DI container configuration in section setup. Also here the automatic passing of dependencies is used by autowiring:
services:
-
create: MyClass
setup:
- setCache
Property Injection
Dependencies are passed directly to the property:
class MyClass
{
/** @var Cache */
public $cache;
}
$obj = new MyClass;
$obj->cache = $cache;
This method is considered inappropriate because the property must be declared as public
. Hence, we have no control
over whether the passed dependency will actually be of the specified type and we lose the ability to react to the newly assigned
dependency with our own code, for example to prevent subsequent changes. At the same time, the property becomes part of the public
interface of the class, which may not be desirable.
The setting of the variable is defined in the DI container configuration in section setup:
services:
-
create: MyClass
setup:
- $cache = @\Cache
Inject
While the previous three methods are generally valid in all object-oriented languages, injecting by method, annotation or inject attribute is specific to Nette presenters. They are discussed in a separate chapter.
Which Way to Choose?
- constructor is suitable for mandatory dependencies that the class needs to function
- the setter, on the other hand, is suitable for optional dependencies, or dependencies that can be changed
- public variables are not recommended