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 valorbool()
,int()
,float()
,string()
para conversão de tipos sem perdastyped()
para gerar uma matriz de todos os serviços de um tipo especificadotagged()
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