Szolgáltatások definiálása

A konfiguráció az a hely, ahol megtanítjuk a DI konténernek, hogyan állítsa össze az egyes szolgáltatásokat, és hogyan kapcsolja össze őket más függőségekkel. A Nette nagyon áttekinthető és elegáns módot kínál ennek elérésére.

A services szekció a NEON formátumú konfigurációs fájlban az a hely, ahol saját szolgáltatásainkat és azok konfigurációját definiáljuk. Nézzünk egy egyszerű példát egy database nevű szolgáltatás definíciójára, amely egy PDO osztály példányát reprezentálja:

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

A megadott konfiguráció a következő factory metódust eredményezi a DI konténerben:

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

A szolgáltatásnevek lehetővé teszik, hogy a konfigurációs fájl más részeiben hivatkozzunk rájuk, @szolgaltatasNev formátumban. Ha nincs szükség a szolgáltatás elnevezésére, egyszerűen használhatunk csak egy kötőjelet:

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

A szolgáltatás lekéréséhez a DI konténerből használhatjuk a getService() metódust a szolgáltatás nevével paraméterként, vagy a getByType() metódust a szolgáltatás típusával:

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

Szolgáltatás létrehozása

Legtöbbször egyszerűen úgy hozunk létre egy szolgáltatást, hogy létrehozunk egy példányt egy adott osztályból. Például:

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

Ha a konfigurációt további kulcsokkal kell bővítenünk, a definíciót több sorba is szétírhatjuk:

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

A create kulcsnak van egy factory aliasa, mindkét változat gyakori a gyakorlatban. Azonban javasoljuk a create használatát.

A konstruktor vagy a létrehozó metódus argumentumai alternatívaként az arguments kulcsban is megadhatók:

services:
	database:
		create: PDO
		arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]

A szolgáltatásokat nemcsak egyszerű osztálypéldányosítással lehet létrehozni, hanem statikus metódusok vagy más szolgáltatások metódusainak hívásának eredményeként is:

services:
	database: DatabaseFactory::create()
	router: @routerFactory::create()

Vegyük észre, hogy az egyszerűség kedvéért -> helyett :: használatos, lásd kifejező eszközök. Ezek a factory metódusok generálódnak:

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

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

A DI konténernek ismernie kell a létrehozott szolgáltatás típusát. Ha egy olyan metódussal hozunk létre szolgáltatást, amelynek nincs megadva visszatérési típusa, akkor ezt a típust explicit módon meg kell adnunk a konfigurációban:

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

Argumentumok

A konstruktoroknak és metódusoknak argumentumokat adunk át, nagyon hasonlóan magához a PHP-hez:

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

A jobb olvashatóság érdekében az argumentumokat külön sorokba írhatjuk. Ebben az esetben a vesszők használata opcionális:

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

Az argumentumokat el is nevezheti, és akkor nem kell törődnie a sorrendjükkel:

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

Ha ki szeretne hagyni néhány argumentumot, és azok alapértelmezett értékét szeretné használni, vagy egy szolgáltatást szeretne beilleszteni az autowiring segítségével, használjon aláhúzást:

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

Argumentumként átadhatók szolgáltatások, használhatók paraméterek és még sok más, lásd kifejező eszközök.

Setup

A setup szekcióban definiáljuk azokat a metódusokat, amelyeket a szolgáltatás létrehozásakor kell meghívni.

services:
	database:
		create: PDO(%dsn%, %user%, %password%)
		setup:
			- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)

Ez PHP-ban így nézne ki:

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

A metódushívásokon kívül értékeket is átadhatunk a property-knek. Támogatott az elem hozzáadása egy tömbhöz is, amelyet idézőjelek közé kell írni, hogy ne ütközzön a NEON szintaxisával:

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

Ami a PHP kódban a következőképpen nézne ki:

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

A setupban azonban hívhatunk statikus metódusokat vagy más szolgáltatások metódusait is. Ha az aktuális szolgáltatást argumentumként kell átadni, adja meg @self-ként:

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

Vegyük észre, hogy az egyszerűség kedvéért -> helyett :: használatos, lásd kifejező eszközök. Ilyen factory metódus generálódik:

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

Kifejező eszközök

A Nette DI rendkívül gazdag kifejező eszközöket ad nekünk, amelyekkel szinte bármit leírhatunk. A konfigurációs fájlokban így használhatunk paramétereket:

# paraméter
%wwwDir%

# paraméter értéke kulcs alatt
%mailer.user%

# paraméter egy stringen belül
'%wwwDir%/images'

Továbbá objektumokat hozhatunk létre, metódusokat és függvényeket hívhatunk:

# objektum létrehozása
DateTime()

# statikus metódus hívása
Collator::create(%locale%)

# PHP függvény hívása
::getenv(DB_USER)

Hivatkozhatunk szolgáltatásokra akár a nevükkel, akár a típusukkal:

# szolgáltatás név szerint
@database

# szolgáltatás típus szerint
@Nette\Database\Connection

Használhatunk first-class callable szintaxist:

# callback létrehozása, hasonlóan a [@user, logout]-hoz
@user::logout(...)

Használhatunk konstansokat:

# osztály konstans
FilesystemIterator::SKIP_DOTS

# globális konstansot a constant() PHP függvénnyel kapunk
::constant(PHP_VERSION)

A metódushívásokat ugyanúgy lehet láncolni, mint PHP-ban. Csak az egyszerűség kedvéért -> helyett :: használatos:

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

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

Ezeket a kifejezéseket bárhol használhatja, a szolgáltatások létrehozásakor, az argumentumokban, a setup szekcióban vagy a paraméterekben:

parameters:
	ipAddress: @http.request::getRemoteAddress()

services:
	database:
		create: DatabaseFactory::create( @anotherService::getDsn() )
		setup:
			- initialize( ::getenv('DB_USER') )

Speciális függvények

A konfigurációs fájlokban használhatja ezeket a speciális függvényeket:

  • not() érték negálása
  • bool(), int(), float(), string() veszteségmentes típuskonverzió a megadott típusra
  • typed() létrehozza a megadott típusú összes szolgáltatás tömbjét
  • tagged() létrehozza a megadott taggel rendelkező összes szolgáltatás tömbjét
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

A klasszikus PHP típuskonverzióval ellentétben, mint pl. az (int), a veszteségmentes típuskonverzió kivételt dob nem numerikus értékek esetén.

A typed() függvény létrehozza a megadott típusú (osztály vagy interfész) összes szolgáltatás tömbjét. Kihagyja azokat a szolgáltatásokat, amelyeknek ki van kapcsolva az autowiringja. Több típust is meg lehet adni vesszővel elválasztva.

services:
	- BarsDependent( typed(Bar) )

Egy adott típusú szolgáltatások tömbjét argumentumként is átadhatja automatikusan az autowiring segítségével.

A tagged() függvény pedig létrehozza az összes, adott taggel rendelkező szolgáltatás tömbjét. Itt is megadhat több taget vesszővel elválasztva.

services:
	- LoggersDependent( tagged(logger) )

Autowiring

Az autowired kulcs lehetővé teszi az autowiring viselkedésének befolyásolását egy adott szolgáltatásra. Részletekért lásd az autowiringról szóló fejezetet.

services:
	foo:
		create: Foo
		autowired: false     # a foo szolgáltatás ki van zárva az autowiringból

Lazy szolgáltatások

A lazy loading egy technika, amely elhalasztja a szolgáltatás létrehozását egészen addig a pillanatig, amíg valóban szükség van rá. A globális konfigurációban engedélyezhető a lazy létrehozás minden szolgáltatásra egyszerre. Az egyes szolgáltatások esetében ezt a viselkedést felülbírálhatja:

services:
	foo:
		create: Foo
		lazy: false

Ha egy szolgáltatás lazy-ként van definiálva, annak a DI konténerből való lekérésekor egy speciális helyettesítő objektumot kapunk. Ez ugyanúgy néz ki és viselkedik, mint a valódi szolgáltatás, de a tényleges inicializálás (konstruktor és setup hívása) csak bármely metódusának vagy property-jének első hívásakor történik meg.

A lazy loading csak felhasználói osztályokra használható, belső PHP osztályokra nem. PHP 8.4 vagy újabb verziót igényel.

Tagek

A tagek további információk hozzáadására szolgálnak a szolgáltatásokhoz. Egy szolgáltatáshoz egy vagy több taget adhat hozzá:

services:
	foo:
		create: Foo
		tags:
			- cached

A tagek értékeket is hordozhatnak:

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

Ahhoz, hogy megkapja az összes, adott tagekkel rendelkező szolgáltatást, használhatja a tagged() függvényt:

services:
	- LoggersDependent( tagged(logger) )

A DI konténerben lekérheti az összes, adott taggel rendelkező szolgáltatás nevét a findByTag() metódussal:

$names = $container->findByTag('logger');
// $names egy tömb, amely tartalmazza a szolgáltatás nevét és a tag értékét
// pl. ['foo' => 'monolog.logger.event', ...]

Inject mód

Az inject: true jelzővel aktiválódik a függőségek átadása a public property-ken keresztül inject annotációval és az inject*() metódusokkal.

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

Alapértelmezés szerint az inject csak a presenterekre van aktiválva.

Szolgáltatások módosítása

A DI konténer számos szolgáltatást tartalmaz, amelyeket beépített vagy felhasználói kiterjesztés révén adtak hozzá. Módosíthatja ezeknek a szolgáltatásoknak a definícióit közvetlenül a konfigurációban. Például megváltoztathatja az application.application szolgáltatás osztályát, amely alapértelmezés szerint Nette\Application\Application, egy másikra:

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

Az alteration jelző informatív jellegű, és azt jelzi, hogy csak egy meglévő szolgáltatást módosítunk.

Kiegészíthetjük a setupot is:

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

Egy szolgáltatás felülírásakor előfordulhat, hogy el akarjuk távolítani az eredeti argumentumokat, setup elemeket vagy tageket, erre szolgál a reset:

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

Ha el szeretne távolítani egy kiterjesztés által hozzáadott szolgáltatást, azt így teheti meg:

services:
	cache.journal: false
verzió: 3.x