Nette Documentation Preview

syntax
Passando Dependências
*********************

<div class=perex>

Argumentos, ou na terminologia de DI "dependências", podem ser passados para classes das seguintes maneiras principais:

* passagem pelo construtor
* passagem por método (chamado setter)
* configuração de propriedade
* método, anotação ou atributo *inject*

</div>

Agora mostraremos cada variante com exemplos concretos.


Passagem pelo construtor
========================

As dependências são passadas no momento da criação do objeto como argumentos do construtor:

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

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

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

Esta forma é adequada para dependências obrigatórias que a classe necessita essencialmente para sua função, pois sem elas a instância não poderá ser criada.

A partir do PHP 8.0, podemos usar uma forma mais curta de notação ([constructor property promotion |https://blog.nette.org/pt/php-8-0-complete-overview-of-news#toc-constructor-property-promotion]), que é funcionalmente equivalente:

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

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

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

O contêiner de DI passa as dependências para o construtor automaticamente usando [autowiring |autowiring]. Argumentos que não podem ser passados dessa forma (por exemplo, strings, números, booleanos) [escrevemos na configuração |services#Argumentos].


Constructor hell
----------------

O termo *constructor hell* descreve a situação em que um descendente herda de uma classe pai cujo construtor requer dependências, e ao mesmo tempo o descendente requer dependências. Ele também deve receber e passar as dependências do pai:

```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 surge no momento em que queremos alterar o construtor da classe `BaseClass`, por exemplo, quando uma nova dependência é adicionada. Então, é necessário modificar também todos os construtores dos descendentes. O que torna tal modificação um inferno.

Como evitar isso? A solução é **dar preferência à [composição em vez de herança |faq#Por que a composição é preferida em relação à herança]**.

Ou seja, projetaremos o código de forma diferente. Evitaremos classes [abstratas |nette:introduction-to-object-oriented-programming#Classes Abstratas] `Base*`. Em vez de `MyClass` obter certas funcionalidades herdando de `BaseClass`, essa funcionalidade será passada como 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;
	}
}
```


Passagem por setter
===================

As dependências são passadas chamando um método que as armazena em uma propriedade privada. A convenção usual de nomenclatura para esses métodos é a forma `set*()`, por isso são chamados de setters, mas podem, é claro, ter qualquer outro nome.

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

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

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

Este método é adequado para dependências opcionais que não são essenciais para a função da classe, pois não há garantia de que o objeto realmente receberá a dependência (ou seja, que o usuário chamará o método).

Ao mesmo tempo, este método permite chamar o setter repetidamente e, assim, alterar a dependência. Se isso não for desejado, adicionamos uma verificação ao método ou, a partir do PHP 8.1, marcamos a propriedade `$cache` com o sinalizador `readonly`.

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

	public function setCache(Cache $cache): void
	{
		if (isset($this->cache)) {
			throw new \RuntimeException('A dependência já foi definida');
		}
		$this->cache = $cache;
	}
}
```

A chamada do setter é definida na configuração do contêiner de DI na [chave setup |services#Setup]. Aqui também se utiliza a passagem automática de dependências por autowiring:

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


Configuração de propriedade
===========================

As dependências são passadas escrevendo diretamente na propriedade de membro:

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

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

Este método é considerado inadequado porque a propriedade de membro deve ser declarada como `public`. E, portanto, não temos controle sobre se a dependência passada será realmente do tipo especificado (válido antes do PHP 7.4) e perdemos a capacidade de reagir à dependência recém-atribuída com código próprio, por exemplo, para impedir alterações subsequentes. 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 propriedade é definida na configuração do contêiner de DI na [seção setup |services#Setup]:

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


Inject
======

Enquanto os três métodos anteriores se aplicam geralmente em todas as linguagens orientadas a objetos, a injeção por método, anotação ou atributo *inject* é específica puramente para presenters no Nette. Eles são discutidos em um [capítulo separado |best-practices:inject-method-attribute].


Qual método escolher?
=====================

- o construtor é adequado para dependências obrigatórias que a classe necessita essencialmente para sua função
- o setter, por outro lado, é adequado para dependências opcionais, ou dependências que podem ser alteradas posteriormente
- propriedades públicas não são adequadas

Passando Dependências

Argumentos, ou na terminologia de DI „dependências“, podem ser passados para classes das seguintes maneiras principais:

  • passagem pelo construtor
  • passagem por método (chamado setter)
  • configuração de propriedade
  • método, anotação ou atributo inject

Agora mostraremos cada variante com exemplos concretos.

Passagem pelo construtor

As dependências são passadas no momento da criação do objeto como argumentos do construtor:

class MyClass
{
	private Cache $cache;

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

$obj = new MyClass($cache);

Esta forma é adequada para dependências obrigatórias que a classe necessita essencialmente para sua função, pois sem elas a instância não poderá ser criada.

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

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

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

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

O contêiner de DI passa as dependências para o construtor automaticamente usando autowiring. Argumentos que não podem ser passados dessa forma (por exemplo, strings, números, booleanos) escrevemos na configuração.

Constructor hell

O termo constructor hell descreve a situação em que um descendente herda de uma classe pai cujo construtor requer dependências, e ao mesmo tempo o descendente requer dependências. Ele também deve receber e passar as dependências do pai:

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 surge no momento em que queremos alterar o construtor da classe BaseClass, por exemplo, quando uma nova dependência é adicionada. Então, é necessário modificar também todos os construtores dos descendentes. O que torna tal modificação um inferno.

Como evitar isso? A solução é dar preferência à composição em vez de herança.

Ou seja, projetaremos o código de forma diferente. Evitaremos classes abstratas Base*. Em vez de MyClass obter certas funcionalidades herdando de BaseClass, essa funcionalidade será passada como 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;
	}
}

Passagem por setter

As dependências são passadas chamando um método que as armazena em uma propriedade privada. A convenção usual de nomenclatura para esses métodos é a forma set*(), por isso são chamados de setters, mas podem, é claro, ter qualquer outro nome.

class MyClass
{
	private Cache $cache;

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

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

Este método é adequado para dependências opcionais que não são essenciais para a função da classe, pois não há garantia de que o objeto realmente receberá a dependência (ou seja, que o usuário chamará o método).

Ao mesmo tempo, este método permite chamar o setter repetidamente e, assim, alterar a dependência. Se isso não for desejado, adicionamos uma verificação ao método ou, a partir do PHP 8.1, marcamos a propriedade $cache com o sinalizador readonly.

class MyClass
{
	private Cache $cache;

	public function setCache(Cache $cache): void
	{
		if (isset($this->cache)) {
			throw new \RuntimeException('A dependência já foi definida');
		}
		$this->cache = $cache;
	}
}

A chamada do setter é definida na configuração do contêiner de DI na chave setup. Aqui também se utiliza a passagem automática de dependências por autowiring:

services:
	-	create: MyClass
		setup:
			- setCache

Configuração de propriedade

As dependências são passadas escrevendo diretamente na propriedade de membro:

class MyClass
{
	public Cache $cache;
}

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

Este método é considerado inadequado porque a propriedade de membro deve ser declarada como public. E, portanto, não temos controle sobre se a dependência passada será realmente do tipo especificado (válido antes do PHP 7.4) e perdemos a capacidade de reagir à dependência recém-atribuída com código próprio, por exemplo, para impedir alterações subsequentes. 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 propriedade é definida na configuração do contêiner de DI na seção setup:

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

Inject

Enquanto os três métodos anteriores se aplicam geralmente em todas as linguagens orientadas a objetos, a injeção por método, anotação ou atributo inject é específica puramente para presenters no Nette. Eles são discutidos em um capítulo separado.

Qual método escolher?

  • o construtor é adequado para dependências obrigatórias que a classe necessita essencialmente para sua função
  • o setter, por outro lado, é adequado para dependências opcionais, ou dependências que podem ser alteradas posteriormente
  • propriedades públicas não são adequadas