Nette Documentation Preview

syntax
Dependências de passagem
************************

<div class=perex>

Argumentos, ou "dependências" na terminologia DI, podem ser passados às aulas das seguintes maneiras principais:

* passando por construtor
* passando por método (chamado setter)
* definindo uma propriedade
*por método, anotação ou atributo *injectar*

</div>

Vamos agora ilustrar as diferentes variantes com exemplos concretos.


Injeção do construtor .[#toc-constructor-injection]
===================================================

As dependências são passadas como argumentos para o construtor quando o objeto é criado:

```php
class MyClass
{
	private Cache $cache;

	public function __construct(Cache $cache)
	{
		$this->cache = $cache;
	}
}

$obj = new MyClass($cache);
```

Este formulário é útil para as dependências obrigatórias que a classe precisa absolutamente funcionar, pois sem elas a instância não pode ser criada.

Desde o PHP 8.0, podemos usar uma forma mais curta de notação que é funcionalmente equivalente ([constructor property promotion |https://blog.nette.org/pt/php-8-0-visao-geral-completa-das-noticias#toc-constructor-property-promotion]):

```php
// PHP 8.0
class MyClass
{
	public function __construct(
		private Cache $cache,
	) {
	}
}
```

A partir do PHP 8.1, uma propriedade pode ser marcada com uma bandeira `readonly` que declara que o conteúdo da propriedade não mudará:

```php
// PHP 8.1
class MyClass
{
	public function __construct(
		private readonly Cache $cache,
	) {
	}
}
```

Recipiente DI passa automaticamente as dependências para o construtor usando [a fiação automática |autowiring]. Argumentos que não podem ser passados desta forma (por exemplo, strings, números, booleans) [escrevem na configuração |services#Arguments].


Construtor Inferno .[#toc-constructor-hell]
-------------------------------------------

O termo *inferno construtor* refere-se a uma situação em que uma criança herda de uma classe de pais cujo construtor requer dependências, e a criança requer dependências também. Também deve assumir e transmitir as dependências dos pais:

```php
abstract class BaseClass
{
	private Cache $cache;

	public function __construct(Cache $cache)
	{
		$this->cache = $cache;
	}
}

final class MyClass extends BaseClass
{
	private Database $db;

	// ⛔ CONSTRUCTOR HELL
	public function __construct(Cache $cache, Database $db)
	{
		parent::__construct($cache);
		$this->db = $db;
	}
}
```

O problema ocorre quando queremos mudar o construtor da classe `BaseClass`, por exemplo, quando uma nova dependência é acrescentada. Então temos que modificar todos os construtores das crianças também. O que faz de tal modificação um inferno.

Como evitar isso? A solução é **priorizar a [composição em vez da herança** |faq#Why composition is preferred over inheritance].

Portanto, vamos projetar o código de forma diferente. Evitaremos classes `Base*` [abstratas |nette:introduction-to-object-oriented-programming#abstract-classes]. Em vez de `MyClass` obter alguma funcionalidade herdando de `BaseClass`, essa funcionalidade será passada como uma dependência:

```php
final class SomeFunctionality
{
	private Cache $cache;

	public function __construct(Cache $cache)
	{
		$this->cache = $cache;
	}
}

final class MyClass
{
	private SomeFunctionality $sf;
	private Database $db;

	public function __construct(SomeFunctionality $sf, Database $db) // ✅
	{
		$this->sf = $sf;
		$this->db = $db;
	}
}
```


Injeção de setter .[#toc-setter-injection]
==========================================

As dependências são passadas chamando um método que as armazena em uma propriedade privada. A convenção usual de nomes para estes métodos é a forma `set*()`, razão pela qual eles são chamados de setters, mas é claro que eles podem ser chamados de qualquer outra coisa.

```php
class MyClass
{
	private Cache $cache;

	public function setCache(Cache $cache): void
	{
		$this->cache = $cache;
	}
}

$obj = new MyClass;
$obj->setCache($cache);
```

Este método é útil para dependências opcionais que não são necessárias para a função de classe, uma vez que não é garantido que o objeto realmente as receberá (ou seja, que o usuário chamará o método).

Ao mesmo tempo, este método permite que o setter seja chamado repetidamente para mudar a dependência. Se isto não for desejável, acrescente uma verificação ao método, ou a partir do PHP 8.1, marque a propriedade `$cache` com a bandeira `readonly`.

```php
class MyClass
{
	private Cache $cache;

	public function setCache(Cache $cache): void
	{
		if ($this->cache) {
			throw new RuntimeException('The dependency has already been set');
		}
		$this->cache = $cache;
	}
}
```

A chamada do setter é definida na configuração do recipiente DI na [configuração da seção |services#Setup]. Também aqui a passagem automática de dependências é usada por cabeamento automático:

```neon
services:
	-	create: MyClass
		setup:
			- setCache
```


Injeção de propriedade .[#toc-property-injection]
=================================================

As dependências são passadas diretamente para a propriedade:

```php
class MyClass
{
	public Cache $cache;
}

$obj = new MyClass;
$obj->cache = $cache;
```

Este método é considerado inadequado porque a propriedade deve ser declarada como `public`. Assim, não temos controle sobre se a dependência passada será realmente do tipo especificado (isto era verdade antes do PHP 7.4) e perdemos a capacidade de reagir à dependência recém-atribuída com nosso próprio código, por exemplo, para evitar mudanças subseqüentes. Ao mesmo tempo, a propriedade se torna parte da interface pública da classe, o que pode não ser desejável.

A configuração da variável é definida na configuração do recipiente DI na [configuração da seção |services#Setup]:

```neon
services:
	-	create: MyClass
		setup:
			- $cache = @\Cache
```


Injetar .[#toc-inject]
======================

Enquanto os três métodos anteriores são geralmente válidos em todos os idiomas orientados a objetos, a injeção por método, a anotação ou o atributo *injet* é específico para os apresentadores Nette. Eles são discutidos em [um capítulo separado |best-practices:inject-method-attribute].


Qual a maneira de escolher? .[#toc-which-way-to-choose]
=======================================================

- construtor é adequado para as dependências obrigatórias que a classe precisa para funcionar
- o setter, por outro lado, é adequado para dependências opcionais, ou dependências que podem ser alteradas
- variáveis públicas não são recomendadas

Dependências de passagem

Argumentos, ou „dependências“ na terminologia DI, podem ser passados às aulas das seguintes maneiras principais:

  • passando por construtor
  • passando por método (chamado setter)
  • definindo uma propriedade

por método, anotação ou atributo *injectar

Vamos agora ilustrar as diferentes variantes com exemplos concretos.

Injeção do construtor

As dependências são passadas como argumentos para o construtor quando o objeto é criado:

class MyClass
{
	private Cache $cache;

	public function __construct(Cache $cache)
	{
		$this->cache = $cache;
	}
}

$obj = new MyClass($cache);

Este formulário é útil para as dependências obrigatórias que a classe precisa absolutamente funcionar, pois sem elas a instância não pode ser criada.

Desde o PHP 8.0, podemos usar uma forma mais curta de notação que é funcionalmente equivalente (constructor property promotion):

// PHP 8.0
class MyClass
{
	public function __construct(
		private Cache $cache,
	) {
	}
}

A partir do PHP 8.1, uma propriedade pode ser marcada com uma bandeira readonly que declara que o conteúdo da propriedade não mudará:

// PHP 8.1
class MyClass
{
	public function __construct(
		private readonly Cache $cache,
	) {
	}
}

Recipiente DI passa automaticamente as dependências para o construtor usando a fiação automática. Argumentos que não podem ser passados desta forma (por exemplo, strings, números, booleans) escrevem na configuração.

Construtor Inferno

O termo inferno construtor refere-se a uma situação em que uma criança herda de uma classe de pais cujo construtor requer dependências, e a criança requer dependências também. Também deve assumir e transmitir as dependências dos pais:

abstract class BaseClass
{
	private Cache $cache;

	public function __construct(Cache $cache)
	{
		$this->cache = $cache;
	}
}

final class MyClass extends BaseClass
{
	private Database $db;

	// ⛔ CONSTRUCTOR HELL
	public function __construct(Cache $cache, Database $db)
	{
		parent::__construct($cache);
		$this->db = $db;
	}
}

O problema ocorre quando queremos mudar o construtor da classe BaseClass, por exemplo, quando uma nova dependência é acrescentada. Então temos que modificar todos os construtores das crianças também. O que faz de tal modificação um inferno.

Como evitar isso? A solução é priorizar a composição em vez da herança.

Portanto, vamos projetar o código de forma diferente. Evitaremos classes Base* abstratas. Em vez de MyClass obter alguma funcionalidade herdando de BaseClass, essa funcionalidade será passada como uma dependência:

final class SomeFunctionality
{
	private Cache $cache;

	public function __construct(Cache $cache)
	{
		$this->cache = $cache;
	}
}

final class MyClass
{
	private SomeFunctionality $sf;
	private Database $db;

	public function __construct(SomeFunctionality $sf, Database $db) // ✅
	{
		$this->sf = $sf;
		$this->db = $db;
	}
}

Injeção de setter

As dependências são passadas chamando um método que as armazena em uma propriedade privada. A convenção usual de nomes para estes métodos é a forma set*(), razão pela qual eles são chamados de setters, mas é claro que eles podem ser chamados de qualquer outra coisa.

class MyClass
{
	private Cache $cache;

	public function setCache(Cache $cache): void
	{
		$this->cache = $cache;
	}
}

$obj = new MyClass;
$obj->setCache($cache);

Este método é útil para dependências opcionais que não são necessárias para a função de classe, uma vez que não é garantido que o objeto realmente as receberá (ou seja, que o usuário chamará o método).

Ao mesmo tempo, este método permite que o setter seja chamado repetidamente para mudar a dependência. Se isto não for desejável, acrescente uma verificação ao método, ou a partir do PHP 8.1, marque a propriedade $cache com a bandeira readonly.

class MyClass
{
	private Cache $cache;

	public function setCache(Cache $cache): void
	{
		if ($this->cache) {
			throw new RuntimeException('The dependency has already been set');
		}
		$this->cache = $cache;
	}
}

A chamada do setter é definida na configuração do recipiente DI na configuração da seção. Também aqui a passagem automática de dependências é usada por cabeamento automático:

services:
	-	create: MyClass
		setup:
			- setCache

Injeção de propriedade

As dependências são passadas diretamente para a propriedade:

class MyClass
{
	public Cache $cache;
}

$obj = new MyClass;
$obj->cache = $cache;

Este método é considerado inadequado porque a propriedade deve ser declarada como public. Assim, não temos controle sobre se a dependência passada será realmente do tipo especificado (isto era verdade antes do PHP 7.4) e perdemos a capacidade de reagir à dependência recém-atribuída com nosso próprio código, por exemplo, para evitar mudanças subseqüentes. Ao mesmo tempo, a propriedade se torna parte da interface pública da classe, o que pode não ser desejável.

A configuração da variável é definida na configuração do recipiente DI na configuração da seção:

services:
	-	create: MyClass
		setup:
			- $cache = @\Cache

Injetar

Enquanto os três métodos anteriores são geralmente válidos em todos os idiomas orientados a objetos, a injeção por método, a anotação ou o atributo injet é específico para os apresentadores Nette. Eles são discutidos em um capítulo separado.

Qual a maneira de escolher?

  • construtor é adequado para as dependências obrigatórias que a classe precisa para funcionar
  • o setter, por outro lado, é adequado para dependências opcionais, ou dependências que podem ser alteradas
  • variáveis públicas não são recomendadas