Nette Documentation Preview

syntax
Autowiring
**********

.[perex]
Autowiring é um ótimo recurso que pode passar automaticamente os serviços necessários para o construtor e outros métodos, para que não precisemos escrevê-los. Isso economiza muito tempo.

Graças a isso, podemos omitir a grande maioria dos argumentos ao escrever definições de serviço. Em vez de:

```neon
services:
	articles: Model\ArticleRepository(@database, @cache.storage)
```

Basta escrever:

```neon
services:
	articles: Model\ArticleRepository
```

O Autowiring é orientado por tipos, então para funcionar, a classe `ArticleRepository` deve ser definida aproximadamente assim:

```php
namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}
```

Para poder usar o autowiring, deve haver **exatamente um serviço** para cada tipo no contêiner. Se houver mais, o autowiring não saberá qual passar e lançará uma exceção:

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # LANÇARÁ EXCEÇÃO, tanto mainDb quanto tempDb correspondem
```

A solução seria contornar o autowiring e especificar explicitamente o nome do serviço (ou seja, `articles: Model\ArticleRepository(@mainDb)`). Mas é mais inteligente [desativar |#Desativação do autowiring] o autowiring para um dos serviços, ou [dar preferência |#Preferência de autowiring] ao primeiro serviço.


Desativação do autowiring
-------------------------

Podemos desativar o autowiring de um serviço usando a opção `autowired: no`:

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

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # o serviço tempDb é excluído do autowiring

	articles: Model\ArticleRepository  # portanto, passa mainDb para o construtor
```

O serviço `articles` não lançará uma exceção dizendo que existem dois serviços do tipo `PDO` correspondentes (ou seja, `mainDb` e `tempDb`) que podem ser passados para o construtor, porque ele vê apenas o serviço `mainDb`.

.[note]
A configuração do autowiring no Nette funciona de forma diferente do Symfony, onde a opção `autowire: false` diz que o autowiring não deve ser usado para os argumentos do construtor do serviço fornecido. No Nette, o autowiring é sempre usado, seja para argumentos do construtor ou para quaisquer outros métodos. A opção `autowired: false` diz que a instância do serviço fornecido não deve ser passada para lugar nenhum usando autowiring.


Preferência de autowiring
-------------------------

Se tivermos vários serviços do mesmo tipo e especificarmos a opção `autowired` para um deles, esse serviço se torna o preferido:

```neon
services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # torna-se preferido

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository
```

O serviço `articles` não lançará uma exceção dizendo que existem dois serviços do tipo `PDO` correspondentes (ou seja, `mainDb` e `tempDb`), mas usará o serviço preferido, ou seja, `mainDb`.


Array de serviços
-----------------

O Autowiring também pode passar arrays de serviços de um determinado tipo. Como não é possível escrever nativamente o tipo dos itens do array em PHP, é necessário, além do tipo `array`, adicionar um comentário phpDoc com o tipo do item no formato `ClassName[]`:

```php
namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}
```

O contêiner DI então passa automaticamente um array de serviços correspondentes ao tipo fornecido. Ele omite serviços que têm o autowiring desativado.

O tipo no comentário também pode estar no formato `array<int, Class>` ou `list<Class>`. Se você não pode influenciar a forma do comentário phpDoc, pode passar o array de serviços diretamente na configuração usando [`typed()` |services#Funções especiais].


Argumentos escalares
--------------------

O Autowiring só pode injetar objetos e arrays de objetos. Argumentos escalares (por exemplo, strings, números, booleanos) [são escritos na configuração |services#Argumentos]. Uma alternativa é criar um [objeto de configurações |best-practices:passing-settings-to-presenters], que encapsula o valor escalar (ou múltiplos valores) em um objeto, que pode então ser passado novamente usando autowiring.

```php
class MySettings
{
	public function __construct(
		// readonly pode ser usado a partir do PHP 8.1
		public readonly bool $value,
	)
	{}
}
```

Você cria um serviço a partir dele adicionando-o à configuração:

```neon
services:
	- MySettings('any value')
```

Todas as classes então o solicitarão usando autowiring.


Restringindo o autowiring
-------------------------

Para serviços individuais, o autowiring pode ser restrito a certas classes ou interfaces.

Normalmente, o autowiring passa o serviço para cada parâmetro de método cujo tipo o serviço corresponde. Restringir significa que estabelecemos condições que os tipos especificados nos parâmetros do método devem satisfazer para que o serviço seja passado para eles.

Vamos ilustrar com um exemplo:

```php
class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

Se registrássemos todos eles como serviços, o autowiring falharia:

```neon
services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # LANÇARÁ EXCEÇÃO, os serviços parent e child correspondem
	childDep: ChildDependent    # autowiring passa o serviço child para o construtor
```

O serviço `parentDep` lançará a exceção `Multiple services of type ParentClass found: parent, child`, porque ambos os serviços `parent` e `child` se encaixam em seu construtor, e o autowiring não pode decidir qual escolher.

Para o serviço `child`, podemos, portanto, restringir seu autowiring ao tipo `ChildClass`:

```neon
services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # também pode escrever 'autowired: self'

	parentDep: ParentDependent  # autowiring passa o serviço parent para o construtor
	childDep: ChildDependent    # autowiring passa o serviço child para o construtor
```

Agora, o serviço `parent` é passado para o construtor do serviço `parentDep`, porque agora é o único objeto correspondente. O autowiring não passa mais o serviço `child` para lá. Sim, o serviço `child` ainda é do tipo `ParentClass`, mas a condição restritiva dada para o tipo do parâmetro não é mais válida, ou seja, não é verdade que `ParentClass` *é um supertipo de* `ChildClass`.

Para o serviço `child`, `autowired: ChildClass` também poderia ser escrito como `autowired: self`, já que `self` é um placeholder para a classe do serviço atual.

Na chave `autowired`, também é possível especificar várias classes ou interfaces como um array:

```neon
autowired: [BarClass, FooInterface]
```

Vamos tentar complementar o exemplo com interfaces:

```php
interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

Se não restringirmos o serviço `child` de forma alguma, ele se encaixará nos construtores de todas as classes `FooDependent`, `BarDependent`, `ParentDependent` e `ChildDependent`, e o autowiring o passará para lá.

No entanto, se restringirmos seu autowiring a `ChildClass` usando `autowired: ChildClass` (ou `self`), o autowiring o passará apenas para o construtor de `ChildDependent`, porque ele requer um argumento do tipo `ChildClass` e é verdade que `ChildClass` *é do tipo* `ChildClass`. Nenhum outro tipo especificado nos outros parâmetros é um supertipo de `ChildClass`, então o serviço não é passado.

Se o restringirmos a `ParentClass` usando `autowired: ParentClass`, ele será novamente passado para o construtor de `ChildDependent` (porque o `ChildClass` exigido é um supertipo de `ParentClass`) e, agora também para o construtor de `ParentDependent`, porque o tipo `ParentClass` exigido também é adequado.

Se o restringirmos a `FooInterface`, ele ainda será autowired para `ParentDependent` (o `ParentClass` exigido é um supertipo de `FooInterface`) e `ChildDependent`, mas adicionalmente também para o construtor de `FooDependent`, mas não para `BarDependent`, porque `BarInterface` não é um supertipo de `FooInterface`.

```neon
services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # autowiring passa child para o construtor
	barDep: BarDependent        # LANÇARÁ EXCEÇÃO, nenhum serviço corresponde
	parentDep: ParentDependent  # autowiring passa child para o construtor
	childDep: ChildDependent    # autowiring passa child para o construtor
```

Autowiring

Autowiring é um ótimo recurso que pode passar automaticamente os serviços necessários para o construtor e outros métodos, para que não precisemos escrevê-los. Isso economiza muito tempo.

Graças a isso, podemos omitir a grande maioria dos argumentos ao escrever definições de serviço. Em vez de:

services:
	articles: Model\ArticleRepository(@database, @cache.storage)

Basta escrever:

services:
	articles: Model\ArticleRepository

O Autowiring é orientado por tipos, então para funcionar, a classe ArticleRepository deve ser definida aproximadamente assim:

namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}

Para poder usar o autowiring, deve haver exatamente um serviço para cada tipo no contêiner. Se houver mais, o autowiring não saberá qual passar e lançará uma exceção:

services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # LANÇARÁ EXCEÇÃO, tanto mainDb quanto tempDb correspondem

A solução seria contornar o autowiring e especificar explicitamente o nome do serviço (ou seja, articles: Model\ArticleRepository(@mainDb)). Mas é mais inteligente desativar o autowiring para um dos serviços, ou dar preferência ao primeiro serviço.

Desativação do autowiring

Podemos desativar o autowiring de um serviço usando a opção autowired: no:

services:
	mainDb: PDO(%dsn%, %user%, %password%)

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # o serviço tempDb é excluído do autowiring

	articles: Model\ArticleRepository  # portanto, passa mainDb para o construtor

O serviço articles não lançará uma exceção dizendo que existem dois serviços do tipo PDO correspondentes (ou seja, mainDb e tempDb) que podem ser passados para o construtor, porque ele vê apenas o serviço mainDb.

A configuração do autowiring no Nette funciona de forma diferente do Symfony, onde a opção autowire: false diz que o autowiring não deve ser usado para os argumentos do construtor do serviço fornecido. No Nette, o autowiring é sempre usado, seja para argumentos do construtor ou para quaisquer outros métodos. A opção autowired: false diz que a instância do serviço fornecido não deve ser passada para lugar nenhum usando autowiring.

Preferência de autowiring

Se tivermos vários serviços do mesmo tipo e especificarmos a opção autowired para um deles, esse serviço se torna o preferido:

services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # torna-se preferido

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository

O serviço articles não lançará uma exceção dizendo que existem dois serviços do tipo PDO correspondentes (ou seja, mainDb e tempDb), mas usará o serviço preferido, ou seja, mainDb.

Array de serviços

O Autowiring também pode passar arrays de serviços de um determinado tipo. Como não é possível escrever nativamente o tipo dos itens do array em PHP, é necessário, além do tipo array, adicionar um comentário phpDoc com o tipo do item no formato ClassName[]:

namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}

O contêiner DI então passa automaticamente um array de serviços correspondentes ao tipo fornecido. Ele omite serviços que têm o autowiring desativado.

O tipo no comentário também pode estar no formato array<int, Class> ou list<Class>. Se você não pode influenciar a forma do comentário phpDoc, pode passar o array de serviços diretamente na configuração usando typed().

Argumentos escalares

O Autowiring só pode injetar objetos e arrays de objetos. Argumentos escalares (por exemplo, strings, números, booleanos) são escritos na configuração. Uma alternativa é criar um objeto de configurações, que encapsula o valor escalar (ou múltiplos valores) em um objeto, que pode então ser passado novamente usando autowiring.

class MySettings
{
	public function __construct(
		// readonly pode ser usado a partir do PHP 8.1
		public readonly bool $value,
	)
	{}
}

Você cria um serviço a partir dele adicionando-o à configuração:

services:
	- MySettings('any value')

Todas as classes então o solicitarão usando autowiring.

Restringindo o autowiring

Para serviços individuais, o autowiring pode ser restrito a certas classes ou interfaces.

Normalmente, o autowiring passa o serviço para cada parâmetro de método cujo tipo o serviço corresponde. Restringir significa que estabelecemos condições que os tipos especificados nos parâmetros do método devem satisfazer para que o serviço seja passado para eles.

Vamos ilustrar com um exemplo:

class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

Se registrássemos todos eles como serviços, o autowiring falharia:

services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # LANÇARÁ EXCEÇÃO, os serviços parent e child correspondem
	childDep: ChildDependent    # autowiring passa o serviço child para o construtor

O serviço parentDep lançará a exceção Multiple services of type ParentClass found: parent, child, porque ambos os serviços parent e child se encaixam em seu construtor, e o autowiring não pode decidir qual escolher.

Para o serviço child, podemos, portanto, restringir seu autowiring ao tipo ChildClass:

services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # também pode escrever 'autowired: self'

	parentDep: ParentDependent  # autowiring passa o serviço parent para o construtor
	childDep: ChildDependent    # autowiring passa o serviço child para o construtor

Agora, o serviço parent é passado para o construtor do serviço parentDep, porque agora é o único objeto correspondente. O autowiring não passa mais o serviço child para lá. Sim, o serviço child ainda é do tipo ParentClass, mas a condição restritiva dada para o tipo do parâmetro não é mais válida, ou seja, não é verdade que ParentClass é um supertipo de ChildClass.

Para o serviço child, autowired: ChildClass também poderia ser escrito como autowired: self, já que self é um placeholder para a classe do serviço atual.

Na chave autowired, também é possível especificar várias classes ou interfaces como um array:

autowired: [BarClass, FooInterface]

Vamos tentar complementar o exemplo com interfaces:

interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

Se não restringirmos o serviço child de forma alguma, ele se encaixará nos construtores de todas as classes FooDependent, BarDependent, ParentDependent e ChildDependent, e o autowiring o passará para lá.

No entanto, se restringirmos seu autowiring a ChildClass usando autowired: ChildClass (ou self), o autowiring o passará apenas para o construtor de ChildDependent, porque ele requer um argumento do tipo ChildClass e é verdade que ChildClass é do tipo ChildClass. Nenhum outro tipo especificado nos outros parâmetros é um supertipo de ChildClass, então o serviço não é passado.

Se o restringirmos a ParentClass usando autowired: ParentClass, ele será novamente passado para o construtor de ChildDependent (porque o ChildClass exigido é um supertipo de ParentClass) e, agora também para o construtor de ParentDependent, porque o tipo ParentClass exigido também é adequado.

Se o restringirmos a FooInterface, ele ainda será autowired para ParentDependent (o ParentClass exigido é um supertipo de FooInterface) e ChildDependent, mas adicionalmente também para o construtor de FooDependent, mas não para BarDependent, porque BarInterface não é um supertipo de FooInterface.

services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # autowiring passa child para o construtor
	barDep: BarDependent        # LANÇARÁ EXCEÇÃO, nenhum serviço corresponde
	parentDep: ParentDependent  # autowiring passa child para o construtor
	childDep: ChildDependent    # autowiring passa child para o construtor