Fábricas Geradas
Nette DI pode gerar automaticamente o código de fábrica com base na interface, o que poupa você de escrever o código.
Uma fábrica é uma classe que cria e configura objetos. Portanto, ela passa suas dependências também para eles. Por favor, não confunda com o padrão de design método de fábrica, que descreve uma maneira específica de usar as fábricas e não está relacionado a este tópico.
Mostramos como é uma fábrica desse tipo 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 código de fábrica automaticamente. Tudo que você precisa fazer é criar uma interface e a Nette DI
irá gerar uma implementação. A interface deve ter exatamente um método chamado create
e declarar um tipo de
retorno:
interface ArticleFactory
{
function create(): Article;
}
Assim, a fábrica ArticleFactory
tem um método create
que cria objetos Article
.
A classe Article
pode se parecer, por exemplo, com a seguinte:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
}
Acrescentar a fábrica ao arquivo de configuração:
services:
- ArticleFactory
Nette DI irá gerar a implementação da fábrica correspondente.
Assim, no código que utiliza a fábrica, solicitamos o objeto por interface e a Nette DI utiliza a implementação gerada:
class UserController
{
public function __construct(
private ArticleFactory $articleFactory,
) {
}
public function foo()
{
// deixar a fábrica criar um objeto
$article = $this->articleFactory->create();
}
}
Fábrica parametrizada
O método de fábrica create
pode aceitar parâmetros que depois passa para o construtor. Por exemplo, vamos
adicionar uma identificação do autor do artigo à classe Article
:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
private int $authorId,
) {
}
}
Acrescentaremos também o parâmetro à fábrica:
interface ArticleFactory
{
function create(int $authorId): Article;
}
Como o parâmetro no construtor e o parâmetro na fábrica têm o mesmo nome, a Nette DI os passará automaticamente.
Definição avançada
A definição também pode ser escrita em várias linhas usando a chave implement
:
services:
articleFactory:
implement: ArticleFactory
Ao escrever desta forma mais longa, é possível fornecer argumentos adicionais para o construtor na chave
arguments
e configuração adicional usando setup
, assim como para serviços normais.
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 Article
:
services:
articleFactory:
implement: ArticleFactory
arguments:
authorId: 123
Ou, inversamente, se create()
aceitasse o parâmetro $authorId
mas não fizesse parte do construtor
e fosse aprovado pelo método Article::setAuthorId()
, referir-nos-íamos 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 acessores. O acessor é um objeto com método get()
devolvendo um determinado serviço a partir do contêiner DI. Várias chamadas get()
retornarão sempre a mesma
instância.
Os acessores trazem a carga preguiçosa para as dependências. Vamos ter um registro de erros de classe em um banco de dados
especial. Se a conexão de banco de dados fosse passada como uma dependência em seu construtor, a conexão precisaria ser sempre
criada, embora só fosse usada raramente quando um erro aparecesse, de modo que a conexão permaneceria, na maioria das vezes, sem
uso. Em vez disso, a classe pode passar por um acessor e quando seu método get()
é chamado, somente então
o objeto banco de dados é criado:
Como criar um acessor? Escreva apenas uma interface e a Nette DI irá gerar a implementação. A interface deve ter exatamente
um método chamado get
e deve declarar o tipo de retorno:
interface PDOAccessor
{
function get(): PDO;
}
Adicione o acessor ao arquivo de configuração junto com a definição do serviço que o acessor retornará:
services:
- PDOAccessor
- PDO(%dsn%, %user%, %password%)
O acessor devolve um serviço do tipo PDO
e como só existe um serviço desse tipo na configuração, o acessor
o devolverá. Com vários serviços desse tipo configurados, você pode especificar qual deles deve ser devolvido usando seu
nome, por exemplo - PDOAccessor(@db1)
.
Multifábrica/Acessor
Até agora, as fábricas e os acessores só podiam criar ou devolver um único objeto. Uma multifábrica combinada com um
acessório também pode ser criada. A interface de tal classe multifatorial pode consistir de múltiplos métodos chamados
create<name>()
e get<name>()
por exemplo:
interface MultiFactory
{
function createArticle(): Article;
function getDb(): PDO;
}
Em vez de passar por múltiplas fábricas e acessores gerados, você pode passar apenas por uma complexa multifábrica.
Como alternativa, você pode usar get()
com um parâmetro em vez de vários métodos:
interface MultiFactoryAlt
{
function get($name): PDO;
}
Nesse caso, MultiFactory::getArticle()
faz a mesma coisa que MultiFactoryAlt::get('article')
.
Entretanto, a sintaxe alternativa tem algumas desvantagens. Não está claro quais valores de $name
são compatíveis
e o tipo de retorno não pode ser especificado na interface ao usar vários valores diferentes de $name
.
Definição com uma lista
Essa maneira pode ser usada para definir uma fábrica múltipla na configuração:
services:
- MultiFactory(
article: Article # defines createArticle()
db: PDO(%dsn%, %user%, %password%) # defines getDb()
)
Ou, na definição da fábrica, podemos nos referir aos serviços existentes usando uma referência:
services:
article: Article
- PDO(%dsn%, %user%, %password%)
- MultiFactory(
article: @article # defines createArticle()
db: @\PDO # defines getDb()
)
Definição com tags
Outra opção para definir uma multifábrica é a utilização de etiquetas:
services:
- App\Core\RouterFactory::createRouter
- App\Model\DatabaseAccessor(
db1: @database.db1.explorer
)