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ónpará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 valores
  • bool(), int(), float(), string() para la conversión de tipos sin pérdidas
  • typed() para generar una matriz de todos los servicios de un tipo especificado
  • tagged() 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
versión: 3.x