Definições de serviço

A configuração é o local em que instruímos o contêiner DI sobre como montar serviços individuais e como conectá-los a outras dependências. O Nette oferece uma maneira muito clara e elegante de fazer isso.

A seção services no arquivo de configuração do NEON é onde definimos nossos serviços personalizados e suas configurações. Vamos dar uma olhada em 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:')

Essa configuração resulta no seguinte método de fábrica no contêiner DI:

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

Os nomes dos serviços nos permitem fazer referência a eles em outras partes do arquivo de configuração, usando o formato @serviceName. Se não houver necessidade de nomear o serviço, podemos simplesmente usar um marcador:

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

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

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

Criação de serviços

Geralmente, criamos um serviço simplesmente instanciando uma classe específica. Por exemplo:

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

Se precisarmos expandir a configuração com chaves adicionais, a definição poderá ser expandida em várias linhas:

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

A chave create tem um alias factory, ambas as versões são comuns na prática. No entanto, recomendamos o uso de create.

Como alternativa, os argumentos do construtor ou o método de criação podem ser escritos 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 instanciação de uma classe; eles também podem resultar 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, em vez de ->, usamos ::, veja a expressão significa. Esses métodos de fábrica são gerados:

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

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

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

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

Argumentos

Passamos argumentos para construtores e métodos de forma muito semelhante ao PHP comum:

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

Para facilitar a leitura, podemos listar os argumentos em linhas separadas. Nesse formato, 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, o que permite que você não se preocupe com a ordem deles:

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

Se quiser omitir determinados argumentos e usar seus valores padrão ou inserir um serviço por meio da conexão automática, use um sublinhado:

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

Os argumentos podem ser serviços, parâmetros e muito mais; consulte os meios de expressão.

Configuração

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)

Em PHP, isso seria parecido com:

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

Além das chamadas de método, você também pode passar valores para as propriedades. A adição de um elemento a uma matriz também é suportada, mas você precisa colocá-la entre aspas para evitar colidir com a sintaxe NEON:

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

Em PHP, isso se traduziria em:

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

Na configuração, você também pode chamar métodos estáticos ou métodos de outros serviços. Se você precisar passar o serviço atual como um argumento, use @self:

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

Observe que, para simplificar, em vez de ->, usamos ::, veja a expressão significa. Isso gera o seguinte método de fábrica:

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

Meios de Expressão

O Nette DI nos fornece recursos de expressão excepcionalmente ricos, o que nos permite articular quase tudo. Nos arquivos de configuração, podemos usar parâmetros:

# parâmetro
%wwwDir%

# valor em uma chave de parâmetro
%mailer.user%

# parâmetro em uma cadeia de caracteres
'%wwwDir%/images'

Também podemos criar objetos, chamar métodos e funções:

# criar um objeto
DateTime()

# chamar um método estático
Collator::create(%locale%)

# chamar uma função PHP
::getenv(DB_USER)

Referir-se aos serviços pelo nome ou pelo tipo:

# serviço por nome
@database

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

Use a sintaxe chamável de primeira classe:

# creating a callback, equivalent to [@user, logout]
@user::logout(...)

Usar constantes:

# constante de classe
FilesystemIterator::SKIP_DOTS

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

As chamadas de método podem ser encadeadas, assim como no PHP. Para simplificar, em vez de ->, usamos :::

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

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

Essas expressões podem ser usadas em qualquer lugar ao criar serviços, em argumentos, na seção de configuração 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 utilizar essas funções especiais:

  • not() para negação de valor
  • bool(), int(), float(), string() para conversão de tipos sem perdas
  • typed() para gerar uma matriz de todos os serviços de um tipo especificado
  • tagged() para criar uma matriz de todos os serviços com uma determinada tag
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

Em comparação com o typecasting convencional em PHP, como (int), o lossless type casting lançará uma exceção para valores não numéricos.

A função typed() cria uma matriz de todos os serviços de um tipo específico (classe ou interface). Ela exclui os serviços com o autowiring desativado. Vários tipos podem ser especificados, separados por vírgulas.

services:
	- BarsDependent( typed(Bar) )

Você também pode passar automaticamente uma matriz de serviços de um tipo específico como um argumento usando o autowiring.

A função tagged() cria uma matriz de todos os serviços com uma tag especificada. Várias tags podem ser listadas, separadas por vírgulas.

services:
	- LoggersDependent( tagged(logger) )

Fiação automática

A chave autowired permite modificar o comportamento de conexão automática de um serviço específico. Para obter mais detalhes, consulte o capítulo Autowiring.

services:
	foo:
		create: Foo
		autowired: false     # O serviço foo está excluído da autocablagem

Tags

As tags são usadas para adicionar informações suplementares aos serviços. Você pode atribuir uma ou mais tags a um serviço:

services:
	foo:
		create: Foo
		tags:
			- cached

As tags também podem conter valores:

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

Para recuperar todos os serviços com tags específicas, você pode usar a função tagged():

services:
	- LoggersDependent( tagged(logger) )

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

$names = $container->findByTag('logger');
// $names é uma matriz que contém o nome do serviço e o valor da tag
// Por exemplo, ['foo' => 'monolog.logger.event', ...]

Modo de injeção

O uso do sinalizador inject: true ativa a passagem de dependências por meio de variáveis públicas com a anotação inject e os métodos inject*().

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

Por padrão, o inject só é ativado para apresentadores.

Modificações no serviço

O contêiner DI contém muitos serviços adicionados por extensões internas 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 é convencionalmente Nette\Application\Application, para outra:

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

O sinalizador alteration é informativo, indicando que estamos apenas modificando um serviço existente.

Também podemos complementar a configuração:

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

Ao substituir um serviço, talvez você queira remover argumentos, itens de configuração ou tags originais, e é aí que o reset se torna útil:

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

Se quiser remover um serviço adicionado por uma extensão, você pode fazer isso da seguinte forma:

services:
	cache.journal: false
versão: 3.x