Generated 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:
class ArticleFactory
{
private Nette\Database\Connection $db;
public function __construct(Nette\Database\Connection $db)
{
$this->db = $db;
}
/** @return Article */
public function create()
{
return new Article($this->db);
}
}
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
{
/** @return Article */
function create();
}
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:
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, $authorId)
{
$this->db = $db;
$this->authorId = $authorId;
}
}
We will also add the parameter to the factory:
interface ArticleFactory
{
/** @return Article */
function create(int $authorId);
}
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
Accessor
Besides factories, Nette can also generate so called accessors. Accessor is an object with get()
method returning
a particular service from the DI container. Multiple get()
calls will always return the same instance.
Accessors bring lazy-loading to dependencies. Let's have a class logging errors to a special database. If the database
connection would be passed as a dependency in its constructor, the connection would need to be always created although it would be
used only rarely when an error appears so the connection would stay mostly unused. Instead, the class can pass an accessor and
when its get()
method is called, only then the database object is created:
How to create an accessor? Write an interface only and Nette DI will generate the implementation. The interface must have
exactly one method called get
and must declare the return type:
interface PDOAccessor
{
function get(): PDO;
}
Add the accessor to the configuration file together with the definition of the service the accessor will return:
services:
- PDOAccessor
- PDO(%dsn%, %user%, %password%)
The accessor returns a service of type PDO
and because there's only one such service in the configuration, the
accessor will return it. With multiple configured services of that type you can specify which one should be returned using its
name, for example - PDOAccessor(@db1)
.