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
version: 3.x 2.x