Nette Documentation Preview

syntax
Definindo Serviços
******************

.[perex]
A configuração é o local onde ensinamos ao contêiner de DI como construir serviços individuais e como conectá-los a outras dependências. O Nette fornece uma maneira muito clara e elegante de conseguir isso.

A seção `services` no arquivo de configuração no formato NEON é onde definimos nossos próprios serviços e suas configurações. Vejamos um exemplo simples de definição de um serviço chamado `database`, que representa uma instância da classe `PDO`:

```neon
services:
	database: PDO('sqlite::memory:')
```

A configuração fornecida resultará no seguinte método de fábrica no [Contêiner de DI|container]:

```php
public function createServiceDatabase(): PDO
{
	return new PDO('sqlite::memory:');
}
```

Os nomes dos serviços nos permitem referenciá-los em outras partes do arquivo de configuração, no formato `@nomeDoServico`. Se não for necessário nomear o serviço, podemos simplesmente usar um marcador:

```neon
services:
	- PDO('sqlite::memory:')
```

Para obter um serviço do contêiner de DI, podemos usar o método `getService()` com o nome do serviço como parâmetro, ou o método `getByType()` com o tipo do serviço:

```php
$database = $container->getService('database');
$database = $container->getByType(PDO::class);
```


Criação do serviço
==================

Geralmente, criamos um serviço simplesmente criando uma instância de uma determinada classe. Por exemplo:

```neon
services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
```

Se precisarmos estender a configuração com outras chaves, a definição pode ser dividida em várias linhas:

```neon
services:
	database:
		create: PDO('sqlite::memory:')
		setup: ...
```

A chave `create` tem um alias `factory`, ambas as variantes são comuns na prática. No entanto, recomendamos usar `create`.

Os argumentos do construtor ou do método de criação podem ser escritos alternativamente na chave `arguments`:

```neon
services:
	database:
		create: PDO
		arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]
```

Os serviços não precisam ser criados apenas pela simples criação de uma instância de classe, eles também podem ser o resultado da chamada de métodos estáticos ou métodos de outros serviços:

```neon
services:
	database: DatabaseFactory::create()
	router: @routerFactory::create()
```

Observe que, para simplificar, `::` é usado em vez de `->`, veja [#expressões]. Os seguintes métodos de fábrica serão gerados:

```php
public function createServiceDatabase(): PDO
{
	return DatabaseFactory::create();
}

public function createServiceRouter(): RouteList
{
	return $this->getService('routerFactory')->create();
}
```

O contêiner de DI precisa saber o tipo do serviço criado. Se criarmos um serviço usando um método que não tem um tipo de retorno especificado, devemos especificar explicitamente esse tipo na configuração:

```neon
services:
	database:
		create: DatabaseFactory::create()
		type: PDO
```


Argumentos
==========

Passamos argumentos para o construtor e métodos de maneira muito semelhante ao próprio PHP:

```neon
services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
```

Para melhor legibilidade, podemos dividir os argumentos em linhas separadas. Nesse caso, o uso de vírgulas é opcional:

```neon
services:
	database: PDO(
		'mysql:host=127.0.0.1;dbname=test'
		root
		secret
	)
```

Você também pode nomear os argumentos e não precisa se preocupar com a ordem deles:

```neon
services:
	database: PDO(
		username: root
		password: secret
		dsn: 'mysql:host=127.0.0.1;dbname=test'
	)
```

Se você quiser omitir alguns argumentos e usar seu valor padrão ou injetar um serviço usando [autowiring|autowiring], use um sublinhado:

```neon
services:
	foo: Foo(_, %appDir%)
```

Como argumentos, é possível passar serviços, usar parâmetros e muito mais, veja [#expressões].


Setup
=====

Na seção `setup`, definimos os métodos que devem ser chamados ao criar o serviço.

```neon
services:
	database:
		create: PDO(%dsn%, %user%, %password%)
		setup:
			- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
```

Isso seria assim em PHP:

```php
public function createServiceDatabase(): PDO
{
	$service = new PDO('...', '...', '...');
	$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	return $service;
}
```

Além de chamar métodos, também é possível passar valores para propriedades. A adição de um elemento a um array também é suportada, o que precisa ser escrito entre aspas para não colidir com a sintaxe NEON:

```neon
services:
	foo:
		create: Foo
		setup:
			- $value = 123
			- '$onClick[]' = [@bar, clickHandler]
```

O que seria assim no código PHP:

```php
public function createServiceFoo(): Foo
{
	$service = new Foo;
	$service->value = 123;
	$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
	return $service;
}
```

No setup, no entanto, também é possível chamar métodos estáticos ou métodos de outros serviços. Se você precisar passar o serviço atual como argumento, indique-o como `@self`:

```neon
services:
	foo:
		create: Foo
		setup:
			- My\Helpers::initializeFoo(@self)
			- @anotherService::setFoo(@self)
```

Observe que, para simplificar, `::` é usado em vez de `->`, veja [#expressões]. O seguinte método de fábrica será gerado:

```php
public function createServiceFoo(): Foo
{
	$service = new Foo;
	My\Helpers::initializeFoo($service);
	$this->getService('anotherService')->setFoo($service);
	return $service;
}
```


Expressões .{expressões}
========================

Nette DI nos dá recursos de expressão extraordinariamente ricos, com os quais podemos escrever quase qualquer coisa. Nos arquivos de configuração, podemos usar [parâmetros |configuration#Parâmetros]:

```neon
# parâmetro
%wwwDir%

# valor do parâmetro sob a chave
%mailer.user%

# parâmetro dentro de uma string
'%wwwDir%/images'
```

Além disso, criar objetos, chamar métodos e funções:

```neon
# criação de objeto
DateTime()

# chamada de método estático
Collator::create(%locale%)

# chamada de função PHP
::getenv(DB_USER)
```

Referenciar serviços pelo nome ou pelo tipo:

```neon
# serviço por nome
@database

# serviço por tipo
@Nette\Database\Connection
```

Usar a sintaxe first-class callable: .{data-version:3.2.0}

```neon
# criação de callback, análogo a [@user, logout]
@user::logout(...)
```

Usar constantes:

```neon
# constante de classe
FilesystemIterator::SKIP_DOTS

# constante global obtida pela função PHP constant()
::constant(PHP_VERSION)
```

As chamadas de método podem ser encadeadas como em PHP. Apenas para simplificar, `::` é usado em vez de `->`:

```neon
DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')

@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()
```

Você pode usar essas expressões em qualquer lugar, ao [criar serviços |#Criação do serviço], em [#argumentos], na seção [#Setup] ou em [parâmetros |configuration#Parâmetros]:

```neon
parameters:
	ipAddress: @http.request::getRemoteAddress()

services:
	database:
		create: DatabaseFactory::create( @anotherService::getDsn() )
		setup:
			- initialize( ::getenv('DB_USER') )
```


Funções especiais
-----------------

Nos arquivos de configuração, você pode usar estas funções especiais:

- `not()` negação do valor
- `bool()`, `int()`, `float()`, `string()` conversão sem perdas para o tipo especificado
- `typed()` cria um array de todos os serviços do tipo especificado
- `tagged()` cria um array de todos os serviços com a tag especificada

```neon
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)
```

Em comparação com a conversão de tipo clássica em PHP, como `(int)`, a conversão sem perdas lançará uma exceção para valores não numéricos.

A função `typed()` cria um array de todos os serviços de um determinado tipo (classe ou interface). Ela omite serviços que têm o autowiring desativado. É possível especificar vários tipos separados por vírgula.

```neon
services:
	- BarsDependent( typed(Bar) )
```

Você também pode passar um array de serviços de um determinado tipo como argumento automaticamente usando [autowiring |autowiring#Array de serviços].

A função `tagged()` então cria um array de todos os serviços com uma determinada tag. Aqui também você pode especificar várias tags separadas por vírgula.

```neon
services:
	- LoggersDependent( tagged(logger) )
```


Autowiring
==========

A chave `autowired` permite influenciar o comportamento do autowiring para um serviço específico. Para detalhes, veja [o capítulo sobre autowiring|autowiring].

```neon
services:
	foo:
		create: Foo
		autowired: false     # o serviço foo é excluído do autowiring
```


Serviços Lazy .{data-version:3.2.4}
===================================

Lazy loading é uma técnica que adia a criação de um serviço até o momento em que ele é realmente necessário. Na configuração global, é possível [habilitar a criação lazy |configuration#Serviços Lazy] para todos os serviços de uma vez. Para serviços individuais, você pode então substituir esse comportamento:

```neon
services:
	foo:
		create: Foo
		lazy: false
```

Quando um serviço é definido como lazy, ao solicitá-lo do contêiner de DI, recebemos um objeto substituto especial. Ele parece e se comporta da mesma forma que o serviço real, mas a inicialização real (chamada do construtor e setup) ocorre apenas na primeira chamada de qualquer um de seus métodos ou propriedades.

.[note]
O lazy loading pode ser usado apenas para classes de usuário, não para classes internas do PHP. Requer PHP 8.4 ou mais recente.


Tags
====

As tags servem para adicionar informações complementares aos serviços. Você pode adicionar uma ou mais tags a um serviço:

```neon
services:
	foo:
		create: Foo
		tags:
			- cached
```

As tags também podem carregar valores:

```neon
services:
	foo:
		create: Foo
		tags:
			logger: monolog.logger.event
```

Para obter todos os serviços com certas tags, você pode usar a função `tagged()`:

```neon
services:
	- LoggersDependent( tagged(logger) )
```

No contêiner de DI, você pode obter os nomes de todos os serviços com uma determinada tag usando o método `findByTag()`:

```php
$names = $container->findByTag('logger');
// $names é um array contendo o nome do serviço e o valor da tag
// por exemplo, ['foo' => 'monolog.logger.event', ...]
```


Modo Inject
===========

Usando o sinalizador `inject: true`, a passagem de dependências é ativada através de propriedades públicas com a anotação [inject |best-practices:inject-method-attribute#Atributos Inject] e métodos [inject*() |best-practices:inject-method-attribute#Métodos inject].

```neon
services:
	articles:
		create: App\Model\Articles
		inject: true
```

Por padrão, `inject` é ativado apenas para presenters.


Modificação de serviços
=======================

O contêiner de DI contém muitos serviços que foram adicionados através de extensões embutidas ou [de usuário|extensions]. Você pode modificar as definições desses serviços diretamente na configuração. Por exemplo, você pode alterar a classe do serviço `application.application`, que por padrão é `Nette\Application\Application`, para outra:

```neon
services:
	application.application:
		create: MyApplication
		alteration: true
```

O sinalizador `alteration` é informativo e indica que estamos apenas modificando um serviço existente.

Também podemos complementar o setup:

```neon
services:
	application.application:
		create: MyApplication
		alteration: true
		setup:
			- '$onStartup[]' = [@resource, init]
```

Ao sobrescrever um serviço, podemos querer remover os argumentos originais, itens de setup ou tags, para o qual usamos `reset`:

```neon
services:
	application.application:
		create: MyApplication
		alteration: true
		reset:
			- arguments
			- setup
			- tags
```

Se você quiser remover um serviço adicionado por uma extensão, pode fazer assim:

```neon
services:
	cache.journal: false
```

Definindo Serviços

A configuração é o local onde ensinamos ao contêiner de DI como construir serviços individuais e como conectá-los a outras dependências. O Nette fornece uma maneira muito clara e elegante de conseguir isso.

A seção services no arquivo de configuração no formato NEON é onde definimos nossos próprios serviços e suas configurações. Vejamos um exemplo simples de definição de um serviço chamado database, que representa uma instância da classe PDO:

services:
	database: PDO('sqlite::memory:')

A configuração fornecida resultará no seguinte método de fábrica no Contêiner de DI:

public function createServiceDatabase(): PDO
{
	return new PDO('sqlite::memory:');
}

Os nomes dos serviços nos permitem referenciá-los em outras partes do arquivo de configuração, no formato @nomeDoServico. Se não for necessário nomear o serviço, podemos simplesmente usar um marcador:

services:
	- PDO('sqlite::memory:')

Para obter um serviço do contêiner de DI, podemos usar o método getService() com o nome do serviço como parâmetro, ou o método getByType() com o tipo do serviço:

$database = $container->getService('database');
$database = $container->getByType(PDO::class);

Criação do serviço

Geralmente, criamos um serviço simplesmente criando uma instância de uma determinada classe. Por exemplo:

services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)

Se precisarmos estender a configuração com outras chaves, a definição pode ser dividida em várias linhas:

services:
	database:
		create: PDO('sqlite::memory:')
		setup: ...

A chave create tem um alias factory, ambas as variantes são comuns na prática. No entanto, recomendamos usar create.

Os argumentos do construtor ou do método de criação podem ser escritos alternativamente na chave arguments:

services:
	database:
		create: PDO
		arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]

Os serviços não precisam ser criados apenas pela simples criação de uma instância de classe, eles também podem ser o resultado da chamada de métodos estáticos ou métodos de outros serviços:

services:
	database: DatabaseFactory::create()
	router: @routerFactory::create()

Observe que, para simplificar, :: é usado em vez de ->, veja expressões. Os seguintes métodos de fábrica serão gerados:

public function createServiceDatabase(): PDO
{
	return DatabaseFactory::create();
}

public function createServiceRouter(): RouteList
{
	return $this->getService('routerFactory')->create();
}

O contêiner de DI precisa saber o tipo do serviço criado. Se criarmos um serviço usando um método que não tem um tipo de retorno especificado, devemos especificar explicitamente esse tipo na configuração:

services:
	database:
		create: DatabaseFactory::create()
		type: PDO

Argumentos

Passamos argumentos para o construtor e métodos de maneira muito semelhante ao próprio PHP:

services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)

Para melhor legibilidade, podemos dividir os argumentos em linhas separadas. Nesse caso, o uso de vírgulas é opcional:

services:
	database: PDO(
		'mysql:host=127.0.0.1;dbname=test'
		root
		secret
	)

Você também pode nomear os argumentos e não precisa se preocupar com a ordem deles:

services:
	database: PDO(
		username: root
		password: secret
		dsn: 'mysql:host=127.0.0.1;dbname=test'
	)

Se você quiser omitir alguns argumentos e usar seu valor padrão ou injetar um serviço usando autowiring, use um sublinhado:

services:
	foo: Foo(_, %appDir%)

Como argumentos, é possível passar serviços, usar parâmetros e muito mais, veja expressões.

Setup

Na seção setup, definimos os métodos que devem ser chamados ao criar o serviço.

services:
	database:
		create: PDO(%dsn%, %user%, %password%)
		setup:
			- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)

Isso seria assim em PHP:

public function createServiceDatabase(): PDO
{
	$service = new PDO('...', '...', '...');
	$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	return $service;
}

Além de chamar métodos, também é possível passar valores para propriedades. A adição de um elemento a um array também é suportada, o que precisa ser escrito entre aspas para não colidir com a sintaxe NEON:

services:
	foo:
		create: Foo
		setup:
			- $value = 123
			- '$onClick[]' = [@bar, clickHandler]

O que seria assim no código PHP:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	$service->value = 123;
	$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
	return $service;
}

No setup, no entanto, também é possível chamar métodos estáticos ou métodos de outros serviços. Se você precisar passar o serviço atual como argumento, indique-o como @self:

services:
	foo:
		create: Foo
		setup:
			- My\Helpers::initializeFoo(@self)
			- @anotherService::setFoo(@self)

Observe que, para simplificar, :: é usado em vez de ->, veja expressões. O seguinte método de fábrica será gerado:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	My\Helpers::initializeFoo($service);
	$this->getService('anotherService')->setFoo($service);
	return $service;
}

Expressões

Nette DI nos dá recursos de expressão extraordinariamente ricos, com os quais podemos escrever quase qualquer coisa. Nos arquivos de configuração, podemos usar parâmetros:

# parâmetro
%wwwDir%

# valor do parâmetro sob a chave
%mailer.user%

# parâmetro dentro de uma string
'%wwwDir%/images'

Além disso, criar objetos, chamar métodos e funções:

# criação de objeto
DateTime()

# chamada de método estático
Collator::create(%locale%)

# chamada de função PHP
::getenv(DB_USER)

Referenciar serviços pelo nome ou pelo tipo:

# serviço por nome
@database

# serviço por tipo
@Nette\Database\Connection

Usar a sintaxe first-class callable:

# criação de callback, análogo a [@user, logout]
@user::logout(...)

Usar constantes:

# constante de classe
FilesystemIterator::SKIP_DOTS

# constante global obtida pela função PHP constant()
::constant(PHP_VERSION)

As chamadas de método podem ser encadeadas como em PHP. Apenas para simplificar, :: é usado em vez de ->:

DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')

@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()

Você pode usar essas expressões em qualquer lugar, ao criar serviços, em argumentos, na seção Setup ou em parâmetros:

parameters:
	ipAddress: @http.request::getRemoteAddress()

services:
	database:
		create: DatabaseFactory::create( @anotherService::getDsn() )
		setup:
			- initialize( ::getenv('DB_USER') )

Funções especiais

Nos arquivos de configuração, você pode usar estas funções especiais:

  • not() negação do valor
  • bool(), int(), float(), string() conversão sem perdas para o tipo especificado
  • typed() cria um array de todos os serviços do tipo especificado
  • tagged() cria um array de todos os serviços com a tag especificada
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

Em comparação com a conversão de tipo clássica em PHP, como (int), a conversão sem perdas lançará uma exceção para valores não numéricos.

A função typed() cria um array de todos os serviços de um determinado tipo (classe ou interface). Ela omite serviços que têm o autowiring desativado. É possível especificar vários tipos separados por vírgula.

services:
	- BarsDependent( typed(Bar) )

Você também pode passar um array de serviços de um determinado tipo como argumento automaticamente usando autowiring.

A função tagged() então cria um array de todos os serviços com uma determinada tag. Aqui também você pode especificar várias tags separadas por vírgula.

services:
	- LoggersDependent( tagged(logger) )

Autowiring

A chave autowired permite influenciar o comportamento do autowiring para um serviço específico. Para detalhes, veja o capítulo sobre autowiring.

services:
	foo:
		create: Foo
		autowired: false     # o serviço foo é excluído do autowiring

Serviços Lazy

Lazy loading é uma técnica que adia a criação de um serviço até o momento em que ele é realmente necessário. Na configuração global, é possível habilitar a criação lazy para todos os serviços de uma vez. Para serviços individuais, você pode então substituir esse comportamento:

services:
	foo:
		create: Foo
		lazy: false

Quando um serviço é definido como lazy, ao solicitá-lo do contêiner de DI, recebemos um objeto substituto especial. Ele parece e se comporta da mesma forma que o serviço real, mas a inicialização real (chamada do construtor e setup) ocorre apenas na primeira chamada de qualquer um de seus métodos ou propriedades.

O lazy loading pode ser usado apenas para classes de usuário, não para classes internas do PHP. Requer PHP 8.4 ou mais recente.

Tags

As tags servem para adicionar informações complementares aos serviços. Você pode adicionar uma ou mais tags a um serviço:

services:
	foo:
		create: Foo
		tags:
			- cached

As tags também podem carregar valores:

services:
	foo:
		create: Foo
		tags:
			logger: monolog.logger.event

Para obter todos os serviços com certas tags, você pode usar a função tagged():

services:
	- LoggersDependent( tagged(logger) )

No contêiner de DI, você pode obter os nomes de todos os serviços com uma determinada tag usando o método findByTag():

$names = $container->findByTag('logger');
// $names é um array contendo o nome do serviço e o valor da tag
// por exemplo, ['foo' => 'monolog.logger.event', ...]

Modo Inject

Usando o sinalizador inject: true, a passagem de dependências é ativada através de propriedades públicas com a anotação inject e métodos inject*().

services:
	articles:
		create: App\Model\Articles
		inject: true

Por padrão, inject é ativado apenas para presenters.

Modificação de serviços

O contêiner de DI contém muitos serviços que foram adicionados através de extensões embutidas ou de usuário. Você pode modificar as definições desses serviços diretamente na configuração. Por exemplo, você pode alterar a classe do serviço application.application, que por padrão é Nette\Application\Application, para outra:

services:
	application.application:
		create: MyApplication
		alteration: true

O sinalizador alteration é informativo e indica que estamos apenas modificando um serviço existente.

Também podemos complementar o setup:

services:
	application.application:
		create: MyApplication
		alteration: true
		setup:
			- '$onStartup[]' = [@resource, init]

Ao sobrescrever um serviço, podemos querer remover os argumentos originais, itens de setup ou tags, para o qual usamos reset:

services:
	application.application:
		create: MyApplication
		alteration: true
		reset:
			- arguments
			- setup
			- tags

Se você quiser remover um serviço adicionado por uma extensão, pode fazer assim:

services:
	cache.journal: false