Definiciones de servicios
La configuración es el lugar donde instruimos al contenedor DI sobre cómo ensamblar servicios individuales y cómo conectarlos con otras dependencias. Nette proporciona una forma muy clara y elegante de conseguirlo.
La sección services
en el archivo de configuración NEON es donde definimos nuestros servicios personalizados y
sus configuraciones. Veamos un ejemplo sencillo de definición de un servicio llamado database
, que representa una
instancia de la clase PDO
:
services:
database: PDO('sqlite::memory:')
Esta configuración resulta en el siguiente método de fábrica en el contenedor DI:
public function createServiceDatabase(): PDO
{
return new PDO('sqlite::memory:');
}
Los nombres de los servicios nos permiten referenciarlos en otras partes del fichero de configuración, utilizando el formato
@serviceName
. Si no hay necesidad de nombrar el servicio, podemos usar simplemente una viñeta:
services:
- PDO('sqlite::memory:')
Para recuperar un servicio del contenedor DI, podemos utilizar el método getService()
con el nombre del servicio
como parámetro, o el método getByType()
con el tipo de servicio:
$database = $container->getService('database');
$database = $container->getByType(PDO::class);
Creación de servicios
Lo más común es crear un servicio simplemente instanciando una clase específica. Por ejemplo:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Si necesitamos ampliar la configuración con claves adicionales, la definición puede ampliarse en varias líneas:
services:
database:
create: PDO('sqlite::memory:')
setup: ...
La clave create
tiene un alias factory
, ambas versiones son comunes en la práctica. No obstante,
recomendamos utilizar create
.
Los argumentos del constructor o el método de creación pueden escribirse alternativamente en la clave
arguments
:
services:
database:
create: PDO
arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]
Los servicios no tienen por qué crearse por simple instanciación de una clase; también pueden ser el resultado de llamar a métodos estáticos o a métodos de otros servicios:
services:
database: DatabaseFactory::create()
router: @routerFactory::create()
Nótese que, por simplicidad, en lugar de ->
, utilizamos ::
, véase la expresión significa. Estos métodos de fábrica se generan:
public function createServiceDatabase(): PDO
{
return DatabaseFactory::create();
}
public function createServiceRouter(): RouteList
{
return $this->getService('routerFactory')->create();
}
El contenedor DI necesita conocer el tipo del servicio creado. Si creamos un servicio utilizando un método que no tiene un tipo de retorno especificado, debemos mencionar explícitamente este tipo en la configuración:
services:
database:
create: DatabaseFactory::create()
type: PDO
Argumentos
Pasamos argumentos a los constructores y métodos de una manera muy similar a PHP normal:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Para una mejor legibilidad, podemos listar los argumentos en líneas separadas. En este formato, el uso de comas es opcional:
services:
database: PDO(
'mysql:host=127.0.0.1;dbname=test'
root
secret
)
También puedes nombrar los argumentos, lo que te permitirá despreocuparte de su orden:
services:
database: PDO(
username: root
password: secret
dsn: 'mysql:host=127.0.0.1;dbname=test'
)
Si desea omitir ciertos argumentos y utilizar sus valores por defecto o insertar un servicio mediante autocableado, utilice un guión bajo:
services:
foo: Foo(_, %appDir%)
Los argumentos pueden ser servicios, parámetros y mucho más, véase medios de expresión.
Configuración
En la sección setup
, definimos los métodos que deben ser llamados al crear el servicio.
services:
database:
create: PDO(%dsn%, %user%, %password%)
setup:
- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
En PHP, esto se vería así:
public function createServiceDatabase(): PDO
{
$service = new PDO('...', '...', '...');
$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $service;
}
Además de llamadas a métodos, también puede pasar valores a propiedades. También se puede añadir un elemento a un array, pero es necesario encerrarlo entre comillas para evitar colisiones con la sintaxis NEON:
services:
foo:
create: Foo
setup:
- $value = 123
- '$onClick[]' = [@bar, clickHandler]
En PHP, esto se traduciría como:
public function createServiceFoo(): Foo
{
$service = new Foo;
$service->value = 123;
$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
return $service;
}
En la configuración, también puede llamar a métodos estáticos o métodos de otros servicios. Si necesita pasar el servicio
actual como argumento, utilice @self
:
services:
foo:
create: Foo
setup:
- My\Helpers::initializeFoo(@self)
- @anotherService::setFoo(@self)
Tenga en cuenta que, para simplificar, en lugar de ->
, utilizamos ::
, véase la expresión significa. Esto genera el siguiente método de fábrica:
public function createServiceFoo(): Foo
{
$service = new Foo;
My\Helpers::initializeFoo($service);
$this->getService('anotherService')->setFoo($service);
return $service;
}
Medios de expresión
Nette DI nos proporciona capacidades de expresión excepcionalmente ricas, permitiéndonos articular casi cualquier cosa. En los archivos de configuración, podemos utilizar parámetros:
# parámetro
%wwwDir%
# valor bajo una clave de parámetro
%mailer.user%
# parámetro dentro de una cadena
'%wwwDir%/images'
También podemos crear objetos, llamar a métodos y funciones:
# crear un objeto
DateTime()
# llamar a un método estático
Collator::create(%locale%)
# llamar a una función PHP
::getenv(DB_USER)
Referirse a los servicios por su nombre o por su tipo:
# servicio por nombre
@database
# servicio por tipo
@Nette\Database\Connection
Utilizar sintaxis de llamada de primera clase:
# creating a callback, equivalent to [@user, logout]
@user::logout(...)
Utilizar constantes:
# constante de clase
FilesystemIterator::SKIP_DOTS
# constante global obtenida por la función PHP constant()
::constant(PHP_VERSION)
Las llamadas a métodos pueden encadenarse, como en PHP. Para simplificar, en lugar de ->
, usamos
::
:
DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('A-m-d')
@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()
Estas expresiones se pueden utilizar en cualquier lugar al crear servicios, en argumentos, en la sección de configuración o parámetros:
parameters:
ipAddress: @http.request::getRemoteAddress()
services:
database:
create: DatabaseFactory::create( @anotherService::getDsn() )
setup:
- initialize( ::getenv('DB_USER') )
Funciones especiales
Dentro de los archivos de configuración, puede utilizar estas funciones especiales:
not()
para la negación de valoresbool()
,int()
,float()
,string()
para la conversión de tipos sin pérdidastyped()
para generar una matriz de todos los servicios de un tipo especificadotagged()
para crear una matriz de todos los servicios con una etiqueta determinada
services:
- Foo(
id: int(::getenv('ProjectId'))
productionMode: not(%debugMode%)
)
Comparado con el encasillamiento convencional en PHP, como (int)
, el encasillamiento sin pérdida lanzará una
excepción para valores no numéricos.
La función typed()
crea un array de todos los servicios de un tipo particular (clase o interfaz). Excluye los
servicios con autocableado desactivado. Se pueden especificar varios tipos, separados por comas.
services:
- BarsDependent( typed(Bar) )
También puede pasar automáticamente una matriz de servicios de un tipo específico como argumento utilizando autowiring.
La función tagged()
crea una matriz de todos los servicios con una etiqueta especificada. Se pueden listar varias
etiquetas, separadas por comas.
services:
- LoggersDependent( tagged(logger) )
Autocableado
La tecla autowired
permite modificar el comportamiento del autocableado para un servicio en particular. Para más
detalles, consulte el capítulo sobre autocableado.
services:
foo:
create: Foo
autowired: false # el servicio foo queda excluido del autocableado
Etiquetas
Las etiquetas se utilizan para añadir información complementaria a los servicios. Puede asignar una o varias etiquetas a un servicio:
services:
foo:
create: Foo
tags:
- cached
Las etiquetas también pueden llevar valores:
services:
foo:
create: Foo
tags:
logger: monolog.logger.event
Para recuperar todos los servicios con etiquetas específicas, puede utilizar la función tagged()
:
services:
- LoggersDependent( tagged(logger) )
En el contenedor DI, puede obtener los nombres de todos los servicios con una etiqueta específica utilizando el método
findByTag()
:
$names = $container->findByTag('logger');
// $names es un array que contiene el nombre del servicio y el valor de la etiqueta
// por ejemplo ['foo' => 'monolog.logger.event', ...]
Modo de inyección
El uso de la bandera inject: true
activa el paso de dependencias a través de variables públicas con la
anotación inject y los
métodos inject*().
services:
articles:
create: App\Model\Articles
inject: true
Por defecto, inject
sólo está activado para los presentadores.
Modificaciones del servicio
El contenedor DI contiene muchos servicios añadidos por extensiones incorporadas o de usuario. Puedes modificar las definiciones de estos servicios directamente en la configuración. Por
ejemplo, puede cambiar la clase del servicio application.application
, que convencionalmente es
Nette\Application\Application
, por otra:
services:
application.application:
create: MyApplication
alteration: true
La bandera alteration
es informativa, indicando que simplemente estamos modificando un servicio existente.
También podemos complementar la configuración:
services:
application.application:
create: MyApplication
alteration: true
setup:
- '$onStartup[]' = [@resource, init]
Al sobrescribir un servicio, es posible que desee eliminar los argumentos originales, elementos de configuración o etiquetas,
que es donde reset
es muy útil:
services:
application.application:
create: MyApplication
alteration: true
reset:
- arguments
- setup
- tags
Si deseas eliminar un servicio añadido por una extensión, puedes hacerlo así:
services:
cache.journal: false