Nette Documentation Preview

syntax
Fábricas Geradas
****************

.[perex]
A Nette DI pode gerar automaticamente código de fábricas com base em interfaces, o que economiza a escrita de código.

Uma fábrica é uma classe que produz e configura objetos. Portanto, ela também passa suas dependências para eles. Por favor, não confunda com o padrão de projeto *factory method*, que descreve uma maneira específica de usar fábricas e não está relacionado a este tópico.

Mostramos como é uma fábrica no [capítulo introdutório |introduction#Fábrica]:

```php
class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}
```

A Nette DI pode gerar automaticamente o código das fábricas. Tudo o que você precisa fazer é criar uma interface e a Nette DI gerará a implementação. A interface deve ter exatamente um método chamado `create` e declarar o tipo de retorno:

```php
interface ArticleFactory
{
	function create(): Article;
}
```

Ou seja, a fábrica `ArticleFactory` tem um método `create` que cria objetos `Article`. A classe `Article` pode se parecer com o seguinte:

```php
class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}
```

Adicionamos a fábrica ao arquivo de configuração:

```neon
services:
	- ArticleFactory
```

A Nette DI gerará a implementação correspondente da fábrica.

No código que usa a fábrica, solicitamos o objeto pela interface e a Nette DI usará a implementação gerada:

```php
class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// deixamos a fábrica criar o objeto
		$article = $this->articleFactory->create();
	}
}
```


Fábrica Parametrizada
=====================

O método da fábrica `create` pode aceitar parâmetros, que são então passados para o construtor. Vamos adicionar, por exemplo, o ID do autor do artigo à classe `Article`:

```php
class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}
```

Também adicionamos o parâmetro à fábrica:

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

Graças ao fato de que o parâmetro no construtor e o parâmetro na fábrica têm o mesmo nome, a Nette DI os passa de forma totalmente automática.


Definição Avançada
==================

A definição também pode ser escrita em formato de múltiplas linhas usando a chave `implement`:

```neon
services:
	articleFactory:
		implement: ArticleFactory
```

Ao escrever desta forma mais longa, é possível especificar argumentos adicionais para o construtor na chave `arguments` e configuração adicional usando `setup`, assim como nos serviços comuns.

Exemplo: se o método `create()` não aceitasse o parâmetro `$authorId`, poderíamos especificar um valor fixo na configuração, que seria passado para o construtor de `Article`:

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

Ou, inversamente, se `create()` aceitasse o parâmetro `$authorId`, mas ele não fizesse parte do construtor e fosse passado pelo método `Article::setAuthorId()`, faríamos referência a ele na seção `setup`:

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


Accessor
========

Além das fábricas, a Nette também pode gerar os chamados accessors. São objetos com um método `get()`, que retorna um determinado serviço do contêiner DI. Chamadas repetidas de `get()` retornam sempre a mesma instância.

Os accessors fornecem carregamento preguiçoso (lazy-loading) para dependências. Considere uma classe que registra erros em um banco de dados especial. Se essa classe recebesse a conexão com o banco de dados como dependência via construtor, a conexão sempre teria que ser criada, embora na prática um erro ocorra apenas excepcionalmente e, portanto, na maioria das vezes a conexão permaneceria inutilizada. Em vez disso, a classe recebe um accessor e somente quando seu `get()` é chamado, o objeto do banco de dados é criado:

Como criar um accessor? Basta escrever uma interface e a Nette DI gerará a implementação. A interface deve ter exatamente um método chamado `get` e declarar o tipo de retorno:

```php
interface PDOAccessor
{
	function get(): PDO;
}
```

Adicionamos o accessor ao arquivo de configuração, onde também está a definição do serviço que ele retornará:

```neon
services:
	- PDOAccessor
	- PDO(%dsn%, %user%, %password%)
```

Como o accessor retorna um serviço do tipo `PDO` e há apenas um serviço desse tipo na configuração, ele retornará exatamente esse. Se houvesse mais serviços do tipo fornecido, especificaríamos o serviço retornado usando o nome, por exemplo, `- PDOAccessor(@db1)`.


Fábrica/Accessor Múltiplo
=========================
Nossas fábricas e accessors até agora só podiam produzir ou retornar um objeto. No entanto, é muito fácil criar também fábricas múltiplas combinadas сom accessors. A interface de tal classe conterá qualquer número de métodos com os nomes `create<name>()` e `get<name>()`, por exemplo:

```php
interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}
```

Então, em vez de passar várias fábricas e accessors gerados, passamos uma fábrica mais complexa que pode fazer mais.

Alternativamente, em vez de vários métodos, pode-se usar `get()` сom um parâmetro:

```php
interface MultiFactoryAlt
{
	function get($name): PDO;
}
```

Então, vale que `MultiFactory::createArticle()` faz o mesmo que `MultiFactoryAlt::get('article')`. No entanto, a notação alternativa tem a desvantagem de não ficar claro quais valores de `$name` são suportados e, logicamente, também não é possível na interface distinguir diferentes valores de retorno para diferentes `$name`.


Definição por lista
-------------------
Desta forma, é possível definir uma fábrica múltipla na configuração: .{data-version:3.2.0}

```neon
services:
	- MultiFactory(
		article: Article                      # define createArticle()
		db: PDO(%dsn%, %user%, %password%)    # define getDb()
	)
```

Ou podemos, na definição da fábrica, referir-nos a serviços existentes usando uma referência:

```neon
services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # define createArticle()
		db: @\PDO            # define getDb()
	)
```


Definição usando tags
---------------------

A segunda opção é usar [tags |services#Tags] para a definição:

```neon
services:
	- App\Core\RouterFactory::createRouter # Assumindo que isso é um serviço ou factory
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer # Assumindo que existe um serviço com este nome
	)
```

Fábricas Geradas

A Nette DI pode gerar automaticamente código de fábricas com base em interfaces, o que economiza a escrita de código.

Uma fábrica é uma classe que produz e configura objetos. Portanto, ela também passa suas dependências para eles. Por favor, não confunda com o padrão de projeto factory method, que descreve uma maneira específica de usar fábricas e não está relacionado a este tópico.

Mostramos como é uma fábrica no capítulo introdutório:

class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}

A Nette DI pode gerar automaticamente o código das fábricas. Tudo o que você precisa fazer é criar uma interface e a Nette DI gerará a implementação. A interface deve ter exatamente um método chamado create e declarar o tipo de retorno:

interface ArticleFactory
{
	function create(): Article;
}

Ou seja, a fábrica ArticleFactory tem um método create que cria objetos Article. A classe Article pode se parecer com o seguinte:

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}

Adicionamos a fábrica ao arquivo de configuração:

services:
	- ArticleFactory

A Nette DI gerará a implementação correspondente da fábrica.

No código que usa a fábrica, solicitamos o objeto pela interface e a Nette DI usará a implementação gerada:

class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// deixamos a fábrica criar o objeto
		$article = $this->articleFactory->create();
	}
}

Fábrica Parametrizada

O método da fábrica create pode aceitar parâmetros, que são então passados para o construtor. Vamos adicionar, por exemplo, o ID do autor do artigo à classe Article:

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}

Também adicionamos o parâmetro à fábrica:

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

Graças ao fato de que o parâmetro no construtor e o parâmetro na fábrica têm o mesmo nome, a Nette DI os passa de forma totalmente automática.

Definição Avançada

A definição também pode ser escrita em formato de múltiplas linhas usando a chave implement:

services:
	articleFactory:
		implement: ArticleFactory

Ao escrever desta forma mais longa, é possível especificar argumentos adicionais para o construtor na chave arguments e configuração adicional usando setup, assim como nos serviços comuns.

Exemplo: se o método create() não aceitasse o parâmetro $authorId, poderíamos especificar um valor fixo na configuração, que seria passado para o construtor de Article:

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

Ou, inversamente, se create() aceitasse o parâmetro $authorId, mas ele não fizesse parte do construtor e fosse passado pelo método Article::setAuthorId(), faríamos referência a ele na seção setup:

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

Accessor

Além das fábricas, a Nette também pode gerar os chamados accessors. São objetos com um método get(), que retorna um determinado serviço do contêiner DI. Chamadas repetidas de get() retornam sempre a mesma instância.

Os accessors fornecem carregamento preguiçoso (lazy-loading) para dependências. Considere uma classe que registra erros em um banco de dados especial. Se essa classe recebesse a conexão com o banco de dados como dependência via construtor, a conexão sempre teria que ser criada, embora na prática um erro ocorra apenas excepcionalmente e, portanto, na maioria das vezes a conexão permaneceria inutilizada. Em vez disso, a classe recebe um accessor e somente quando seu get() é chamado, o objeto do banco de dados é criado:

Como criar um accessor? Basta escrever uma interface e a Nette DI gerará a implementação. A interface deve ter exatamente um método chamado get e declarar o tipo de retorno:

interface PDOAccessor
{
	function get(): PDO;
}

Adicionamos o accessor ao arquivo de configuração, onde também está a definição do serviço que ele retornará:

services:
	- PDOAccessor
	- PDO(%dsn%, %user%, %password%)

Como o accessor retorna um serviço do tipo PDO e há apenas um serviço desse tipo na configuração, ele retornará exatamente esse. Se houvesse mais serviços do tipo fornecido, especificaríamos o serviço retornado usando o nome, por exemplo, - PDOAccessor(@db1).

Fábrica/Accessor Múltiplo

Nossas fábricas e accessors até agora só podiam produzir ou retornar um objeto. No entanto, é muito fácil criar também fábricas múltiplas combinadas сom accessors. A interface de tal classe conterá qualquer número de métodos com os nomes create<name>() e get<name>(), por exemplo:

interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}

Então, em vez de passar várias fábricas e accessors gerados, passamos uma fábrica mais complexa que pode fazer mais.

Alternativamente, em vez de vários métodos, pode-se usar get() сom um parâmetro:

interface MultiFactoryAlt
{
	function get($name): PDO;
}

Então, vale que MultiFactory::createArticle() faz o mesmo que MultiFactoryAlt::get('article'). No entanto, a notação alternativa tem a desvantagem de não ficar claro quais valores de $name são suportados e, logicamente, também não é possível na interface distinguir diferentes valores de retorno para diferentes $name.

Definição por lista

Desta forma, é possível definir uma fábrica múltipla na configuração:

services:
	- MultiFactory(
		article: Article                      # define createArticle()
		db: PDO(%dsn%, %user%, %password%)    # define getDb()
	)

Ou podemos, na definição da fábrica, referir-nos a serviços existentes usando uma referência:

services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # define createArticle()
		db: @\PDO            # define getDb()
	)

Definição usando tags

A segunda opção é usar tags para a definição:

services:
	- App\Core\RouterFactory::createRouter # Assumindo que isso é um serviço ou factory
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer # Assumindo que existe um serviço com este nome
	)