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ásabool()
,int()
,float()
,string()
veszteségmentes típuskonverzió a megadott típusratyped()
létrehozza a megadott típusú összes szolgáltatás tömbjéttagged()
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