Definición de servicios
La configuración es el lugar donde enseñamos al contenedor DI cómo debe construir los servicios individuales y cómo conectarlos con otras dependencias. Nette proporciona una forma muy clara y elegante de lograrlo.
La sección services
en el archivo de configuración de formato NEON es el lugar donde definimos nuestros propios
servicios y sus configuraciones. Veamos un ejemplo simple de definición de un servicio llamado database
, que
representa una instancia de la clase PDO
:
services:
database: PDO('sqlite::memory:')
La configuración anterior resultará 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 referirnos a ellos en otras partes del archivo de configuración, en el formato
@nombreServicio
. Si no es necesario nombrar el servicio, podemos simplemente usar una viñeta:
services:
- PDO('sqlite::memory:')
Para obtener un servicio del contenedor DI, podemos usar 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 servicio
Normalmente, creamos un servicio simplemente creando una instancia de una clase determinada. 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 se puede desglosar en varias líneas:
services:
database:
create: PDO('sqlite::memory:')
setup: ...
La clave create
tiene el alias factory
, ambas variantes son comunes en la práctica. Sin embargo,
recomendamos usar create
.
Los argumentos del constructor o del 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 que crearse solo mediante la simple creación de una instancia de clase, también pueden ser el resultado de llamar a métodos estáticos o métodos de otros servicios:
services:
database: DatabaseFactory::create()
router: @routerFactory::create()
Observe que, por simplicidad, se usa ::
en lugar de ->
, consulte expresiones. Se generarán estos métodos de fábrica:
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 mediante un método que no tiene especificado un tipo de retorno, debemos indicar explícitamente este tipo en la configuración:
services:
database:
create: DatabaseFactory::create()
type: PDO
Argumentos
Pasamos argumentos al constructor y a los métodos de una manera muy similar a como se hace en PHP mismo:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Para una mejor legibilidad, podemos desglosar los argumentos en líneas separadas. En tal caso, el uso de comas es opcional:
services:
database: PDO(
'mysql:host=127.0.0.1;dbname=test'
root
secret
)
También puede nombrar los argumentos y no tener que preocuparse por su orden:
services:
database: PDO(
username: root
password: secret
dsn: 'mysql:host=127.0.0.1;dbname=test'
)
Si desea omitir algunos argumentos y usar su valor predeterminado o inyectar un servicio mediante autowiring, use un guion bajo:
services:
foo: Foo(_, %appDir%)
Como argumentos se pueden pasar servicios, usar parámetros y mucho más, consulte expresiones.
Setup
En la sección setup
, definimos los métodos que se deben llamar al crear el servicio.
services:
database:
create: PDO(%dsn%, %user%, %password%)
setup:
- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
Esto se vería así en PHP:
public function createServiceDatabase(): PDO
{
$service = new PDO('...', '...', '...');
$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $service;
}
Además de llamar a métodos, también se pueden pasar valores a las propiedades. También se admite agregar un elemento a un array, lo cual debe escribirse entre comillas para no entrar en conflicto con la sintaxis de NEON:
services:
foo:
create: Foo
setup:
- $value = 123
- '$onClick[]' = [@bar, clickHandler]
Lo que se vería así en el código PHP:
public function createServiceFoo(): Foo
{
$service = new Foo;
$service->value = 123;
$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
return $service;
}
En setup, sin embargo, también se pueden llamar métodos estáticos o métodos de otros servicios. Si necesita pasar el
servicio actual como argumento, indíquelo como @self
:
services:
foo:
create: Foo
setup:
- My\Helpers::initializeFoo(@self)
- @anotherService::setFoo(@self)
Observe que, por simplicidad, se usa ::
en lugar de ->
, consulte expresiones. Se generará tal método de fábrica:
public function createServiceFoo(): Foo
{
$service = new Foo;
My\Helpers::initializeFoo($service);
$this->getService('anotherService')->setFoo($service);
return $service;
}
Expresiones
Nette DI nos proporciona capacidades expresivas extraordinariamente ricas, con las que podemos escribir casi cualquier cosa. En los archivos de configuración, podemos usar parámetros:
# parámetro
%wwwDir%
# valor del parámetro bajo la clave
%mailer.user%
# parámetro dentro de una cadena
'%wwwDir%/images'
Además, crear objetos, llamar a métodos y funciones:
# creación de objeto
DateTime()
# llamada a método estático
Collator::create(%locale%)
# llamada a función PHP
::getenv(DB_USER)
Referirse a servicios ya sea por su nombre o por tipo:
# servicio por nombre
@database
# servicio por tipo
@Nette\Database\Connection
Usar la sintaxis callable de primera clase:
# creación de callback, análogo a [@user, logout]
@user::logout(...)
Usar constantes:
# constante de clase
FilesystemIterator::SKIP_DOTS
# constante global se obtiene con la función PHP constant()
::constant(PHP_VERSION)
Las llamadas a métodos se pueden encadenar igual que en PHP. Solo que, por simplicidad, se usa ::
en lugar de
->
:
DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')
@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()
Puede usar estas expresiones en cualquier lugar, al crear servicios, en argumentos, en la sección setup o en parámetros:
parameters:
ipAddress: @http.request::getRemoteAddress()
services:
database:
create: DatabaseFactory::create( @anotherService::getDsn() )
setup:
- initialize( ::getenv('DB_USER') )
Funciones especiales
En los archivos de configuración puede usar estas funciones especiales:
not()
negación del valorbool()
,int()
,float()
,string()
conversión sin pérdidas al tipo dadotyped()
crea un array de todos los servicios del tipo especificadotagged()
crea un array de todos los servicios con el tag dado
services:
- Foo(
id: int(::getenv('ProjectId'))
productionMode: not(%debugMode%)
)
A diferencia de la conversión de tipos clásica en PHP, como (int)
, la conversión sin pérdidas lanzará una
excepción para valores no numéricos.
La función typed()
crea un array de todos los servicios del tipo dado (clase o interfaz). Omite los servicios
que tienen el autowiring desactivado. Se pueden especificar múltiples tipos separados por comas.
services:
- BarsDependent( typed(Bar) )
También puede pasar un array de servicios de un tipo determinado como argumento automáticamente mediante autowiring.
La función tagged()
crea un array de todos los servicios con un tag determinado. Aquí también puede especificar
múltiples tags separados por comas.
services:
- LoggersDependent( tagged(logger) )
Autowiring
La clave autowired
permite influir en el comportamiento del autowiring para un servicio específico. Para más
detalles, consulte el capítulo sobre autowiring.
services:
foo:
create: Foo
autowired: false # el servicio foo se excluye del autowiring
Servicios Lazy
La carga diferida (lazy loading) es una técnica que pospone la creación de un servicio hasta el momento en que realmente se necesita. En la configuración global, se puede habilitar la creación lazy para todos los servicios a la vez. Para servicios individuales, puede sobrescribir este comportamiento:
services:
foo:
create: Foo
lazy: false
Cuando un servicio se define como lazy, al solicitarlo desde el contenedor DI, obtenemos un objeto proxy especial. Este se ve y se comporta igual que el servicio real, pero la inicialización real (llamada al constructor y setup) ocurre solo en la primera llamada a cualquiera de sus métodos o propiedades.
La carga diferida solo se puede usar para clases de usuario, no para clases internas de PHP. Requiere PHP 8.4 o posterior.
Tags
Los tags sirven para agregar información adicional a los servicios. Puede agregar uno o más tags a un servicio:
services:
foo:
create: Foo
tags:
- cached
Los tags también pueden llevar valores:
services:
foo:
create: Foo
tags:
logger: monolog.logger.event
Para obtener todos los servicios con ciertos tags, puede usar la función tagged()
:
services:
- LoggersDependent( tagged(logger) )
En el contenedor DI, puede obtener los nombres de todos los servicios con un tag determinado usando el método
findByTag()
:
$names = $container->findByTag('logger');
// $names es un array que contiene el nombre del servicio y el valor del tag
// por ej. ['foo' => 'monolog.logger.event', ...]
Modo Inject
Mediante el flag inject: true
se 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
está activado solo para los presenters.
Modificación de servicios
El contenedor DI contiene muchos servicios que fueron agregados mediante extensiones incorporadas o de usuario. Puede modificar las definiciones de estos servicios directamente en la configuración. Por
ejemplo, puede cambiar la clase del servicio application.application
, que es estándarmente
Nette\Application\Application
, por otra:
services:
application.application:
create: MyApplication
alteration: true
El flag alteration
es informativo e indica que solo estamos modificando un servicio existente.
También podemos complementar el setup:
services:
application.application:
create: MyApplication
alteration: true
setup:
- '$onStartup[]' = [@resource, init]
Al sobrescribir un servicio, podemos querer eliminar los argumentos originales, elementos de setup o tags, para lo cual se usa
reset
:
services:
application.application:
create: MyApplication
alteration: true
reset:
- arguments
- setup
- tags
Si desea eliminar un servicio agregado por una extensión, puede hacerlo así:
services:
cache.journal: false