Nette Documentation Preview

DI: Factories

Nette DI can automatically generate factory code based on the interface, which saves you from writing code.

A factory is a class that creates and configures objects. It therefore passes their dependencies to them as well. We showed what such a factory looks like in introduction.

Nette DI can generate factory code automatically. All you have to do is create an interface and Nette DI will generate an implementation. The interface must have exactly one method named create and declare a return type:

interface ArticleFactory
{
	function create(): Article;
}

So the factory ArticleFactory has a method create that creates objects Article. Class Article might look like the following, for example:

class Article
{
	private $db;

	public function __construct(Nette\Database\Connection $db)
	{
		$this->db = $db;
	}
}

Add the factory to the configuration file config.neon:

services:
	- ArticleFactory

Nette DI will generate the corresponding factory implementation.

Thus, in the code that uses the factory, we request the object by interface and Nette DI uses the generated implementation:

class UserController
{
	private $articleFactory;

	public function __construct(ArticleFactory $articleFactory)
	{
		$this->articleFactory = $articleFactory;
	}

	public function foo()
	{
		// let the factory create an object
		$article = $this->articleFactory->create();
	}
}

Parameterized Factory

The factory method create can accept parameters which it then passes to the constructor. For example, let's add an article author ID to the class Article:

class Article
{
	private $db;
	private $authorId;

	public function __construct(Nette\Database\Connection $db, int $authorId)
	{
		$this->db = $db;
		$this->authorId = $authorId;
	}
}

We will also add the parameter to the factory:

interface ArticleFactory
{
	function create(int $authorId): Article;
}

Because the parameter in the constructor and the parameter in the factory have the same name, Nette DI will pass them automatically.

Advanced Definition

The definition can also be written in multi-line form using the key implement:

services:
	articleFactory:
		implement: ArticleFactory

When writing in this longer way, it is possible to provide additional arguments for the constructor in the key arguments and additional configuration using setup, just as for normal services.

Example: if the method create() did not accept the parameter $authorId, we could specify a fixed value in the configuration that would be passed to the constructor Article:

services:
	articleFactory:
		implement: ArticleFactory
		arguments:
			authorId: 123

Or, conversely, if create() did accept the parameter $authorId but it was not part of the constructor and was passed by method Article::setAuthorId(), we would refer to it in section setup:

services:
	articleFactory:
		implement: ArticleFactory
		setup:
			- setAuthorId($authorId)

Component Factory

Consider the following component, which has a dependency on Service:

class MyControl extends Nette\Application\UI\Control
{
	private $service;

	public function __construct(Service $service)
	{
		$this->service = $service;
	}
}

Components are typically created directly in the presenter or parent component code. In this case, Nette DI cannot automatically pass the dependencies, so we get them in the class that creates the component and pass them to the component:

class MyPresenter extends Nette\Application\UI\Presenter
{
	private $service;

	public function __construct(Service $service)
	{
		$this->service = $service;
	}

	protected function createComponentMyControl(): MyControl
	{
		$control = new MyControl($this->service);
		return $control;
	}
}

Since a component may have multiple dependencies, it makes sense to create a factory for the component in these cases as well. Again, we can have it generated based on the interface. For example, our interface for a component might look like this:

interface MyControlFactory
{
	public function create(): MyControl;
}

We register the factory in the configuration file:

services:
	- MyControlFactory

In the presenter, it will then be enough to get the factory and call the method create:

class MyPresenter extends Nette\Application\UI\Presenter
{
	private $myControlFactory;

	public function __construct(MyControlFactory $myControlFactory)
	{
		$this->myControlFactory = $myControlFactory;
	}

	protected function createComponentMyControl(): MyControl
	{
		$control = $this->myControlFactory->create();
		return $control;
	}
}

The created component will be automatically passed its dependencies by the constructor.