Definování služeb
Konfigurace je místem, kde učíme DI kontejner, jak má sestavovat jednotlivé služby a jak je propojovat s dalšími závislostmi. Nette poskytuje velice přehledný a elegantní způsob, jak toho dosáhnout.
Sekce services
v konfiguračním souboru formátu NEON je místem, kde definujeme vlastní služby a jejich
konfigurace. Podívejme se na jednoduchý příklad definice služby pojmenované database
, která reprezentuje
instanci třídy PDO
:
services:
database: PDO('sqlite::memory:')
Uvedená konfigurace vyústí v následující tovární metodu v DI kontejneru:
public function createServiceDatabase(): PDO
{
return new PDO('sqlite::memory:');
}
Názvy služeb nám umožňují odkazovat se na ně v dalších částech konfiguračního souboru, a to ve formátu
@nazevSluzby
. Pokud není potřeba službu pojmenovávat, můžeme jednoduše použít pouze odrážku:
services:
- PDO('sqlite::memory:')
Pro získání služby z DI kontejneru můžeme využít metodu getService()
s názvem služby jako parametrem,
nebo metodu getByType()
s typem služby:
$database = $container->getService('database');
$database = $container->getByType(PDO::class);
Vytvoření služby
Většinou vytváříme službu jednoduše tím, že vytvoříme instanci určité třídy. Například:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Pokud potřebujeme konfiguraci rozšířit o další klíče, lze definici rozepsat do více řádků:
services:
database:
create: PDO('sqlite::memory:')
setup: ...
Klíč create
má alias factory
, obě varianty jsou v praxi běžné. Nicméně doporučujeme
používat create
.
Argumenty konstruktoru nebo vytvářecí metody mohou být alternativně zapsány v klíči arguments
:
services:
database:
create: PDO
arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]
Služby nemusí být vytvářeny jen prostým vytvořením instance třídy, mohou být také výsledkem volání statických metod nebo metod jiných služeb:
services:
database: DatabaseFactory::create()
router: @routerFactory::create()
Všimněte si, že pro jednoduchost se místo ->
používá ::
, viz výrazové prostředky. Vygenerují se tyto tovární metody:
public function createServiceDatabase(): PDO
{
return DatabaseFactory::create();
}
public function createServiceRouter(): RouteList
{
return $this->getService('routerFactory')->create();
}
DI kontejner potřebuje znát typ vytvořené služby. Pokud vytváříme službu pomocí metody, která nemá specifikovaný návratový typ, musíme tento typ explicitně uvést v konfiguraci:
services:
database:
create: DatabaseFactory::create()
type: PDO
Argumenty
Do konstruktoru a metod předáváme argumenty způsobem velmi podobným jako v samotném PHP:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Pro lepší čitelnost můžeme argumenty rozepsat do samostatných řádků. V takovém případě je používání čárek volitelné:
services:
database: PDO(
'mysql:host=127.0.0.1;dbname=test'
root
secret
)
Argumenty můžete také pojmenovat a nemusíte se pak starat o jejich pořadí:
services:
database: PDO(
username: root
password: secret
dsn: 'mysql:host=127.0.0.1;dbname=test'
)
Pokud chcete některé argumenty vynechat a použít jejich výchozí hodnotu nebo dosadit službu pomocí autowiringu, použijte podtržítko:
services:
foo: Foo(_, %appDir%)
Jako argumenty lze předávat služby, používat parametry a mnohem více, viz výrazové prostředky.
Setup
V sekci setup
definujeme metody, které se mají volat při vytváření služby.
services:
database:
create: PDO(%dsn%, %user%, %password%)
setup:
- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
To by v PHP vypadalo takto:
public function createServiceDatabase(): PDO
{
$service = new PDO('...', '...', '...');
$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $service;
}
Kromě volání metod lze také předávat hodnoty do properties. Podporováno je i přidání prvku do pole, které je potřeba zapsat v uvozovkách, aby nekolidovalo se syntaxí NEON:
services:
foo:
create: Foo
setup:
- $value = 123
- '$onClick[]' = [@bar, clickHandler]
Což by v PHP kódu vypadalo následovně:
public function createServiceFoo(): Foo
{
$service = new Foo;
$service->value = 123;
$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
return $service;
}
V setupu lze však volat i statické metody nebo metody jiných služeb. Pokud potřebujete předat jako argument aktuální
službu, uveďte ji jako @self
:
services:
foo:
create: Foo
setup:
- My\Helpers::initializeFoo(@self)
- @anotherService::setFoo(@self)
Všimněte si, že pro jednoduchost se místo ->
používá ::
, viz výrazové prostředky. Vygeneruje se taková tovární metoda:
public function createServiceFoo(): Foo
{
$service = new Foo;
My\Helpers::initializeFoo($service);
$this->getService('anotherService')->setFoo($service);
return $service;
}
Výrazové prostředky
Nette DI nám dává mimořádně bohaté výrazové prostředky, pomocí kterých můžeme zapsat téměř cokoliv. V konfiguračních souborech tak můžeme využívat parametry:
# parametr
%wwwDir%
# hodnota parametru pod klíčem
%mailer.user%
# parametr uvnitř řetězce
'%wwwDir%/images'
Dále vytvářet objekty, volat metody a funkce:
# vytvoření objektu
DateTime()
# volání statické metody
Collator::create(%locale%)
# volání PHP funkce
::getenv(DB_USER)
Odkazovat se na služby buď jejich jménem nebo pomocí typu:
# služba dle názvu
@database
# služba dle typu
@Nette\Database\Connection
Používat first-class callable syntax:
# vytvoření callbacku, obdoba [@user, logout]
@user::logout(...)
Používat konstanty:
# konstanta třídy
FilesystemIterator::SKIP_DOTS
# globální konstantu získáme PHP funkcí constant()
::constant(PHP_VERSION)
Volání metod lze řetězit stejně jako v PHP. Jen pro jednoduchost se místo ->
používá
::
:
DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')
@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()
Tyto výrazy můžete používat kdekoliv, při vytváření služeb, v argumentech, v sekci setup nebo parametrech:
parameters:
ipAddress: @http.request::getRemoteAddress()
services:
database:
create: DatabaseFactory::create( @anotherService::getDsn() )
setup:
- initialize( ::getenv('DB_USER') )
Speciální funkce
V konfiguračních souborech můžete používa tyto speciální funkce:
not()
negace hodnotybool()
,int()
,float()
,string()
bezeztrátové přetypování na daný typtyped()
vytvoří pole všech služeb specifikovaného typutagged()
vytvoření pole všech služeb s daným tagem
services:
- Foo(
id: int(::getenv('ProjectId'))
productionMode: not(%debugMode%)
)
Oproti klasickému přetypování v PHP, jako je např. (int)
, bezeztrátové přetypování vyhodí výjimku pro
nečíselné hodnoty.
Funkce typed()
vytvoří pole všech služeb daného typu (třída nebo rozhraní). Vynechá služby, které mají
vypnutý autowiring. Lze uvést i více typů oddělených čárkou.
services:
- BarsDependent( typed(Bar) )
Pole služeb určitého typu můžete předávat jako argument také automaticky pomocí autowiringu.
Funkce tagged()
pak vytváří pole všech služeb s určitým tagem. I zde můžete specifikovat více tagů
oddělených čárkou.
services:
- LoggersDependent( tagged(logger) )
Autowiring
Klíč autowired
umožňuje ovlivnit chování autowiringu pro konkrétní službu. Pro detaily viz kapitola o autowiringu.
services:
foo:
create: Foo
autowired: false # služba foo je vyřazena z autowiringu
Tagy
Tagy slouží k přidání doplňujících informací k službám. Službě můžete přidat jeden nebo více tagů:
services:
foo:
create: Foo
tags:
- cached
Tagy mohou také nést hodnoty:
services:
foo:
create: Foo
tags:
logger: monolog.logger.event
Aby jste získali všechny služby s určitými tagy, můžete použít funkci tagged()
:
services:
- LoggersDependent( tagged(logger) )
V DI kontejneru můžete získat názvy všech služeb s určitým tagem pomocí metody findByTag()
:
$names = $container->findByTag('logger');
// $names je pole obsahující název služby a hodnotu tagu
// např. ['foo' => 'monolog.logger.event', ...]
Režim Inject
Pomocí příznaku inject: true
se aktivuje předávání závislostí přes veřejné proměnné s anotací inject a metody inject*().
services:
articles:
create: App\Model\Articles
inject: true
Ve výchozím nastavení je inject
aktivováno pouze pro presentery.
Modifikace služeb
DI kontejner obsahuje mnoho služeb, které byly přidány prostřednictvím vestavěného nebo uživatelského rozšíření. Můžete upravit definice těchto služeb přímo v konfiguraci.
Například můžete změnit třídu služby application.application
, což je standardně
Nette\Application\Application
, na jinou:
services:
application.application:
create: MyApplication
alteration: true
Příznak alteration
je informativní a říká, že jen modifikujeme existující službu.
Můžeme také doplnit setup:
services:
application.application:
create: MyApplication
alteration: true
setup:
- '$onStartup[]' = [@resource, init]
Při přepisování služby můžeme chtít odstranit původní argumenty, položky setup nebo tagy, k čemuž slouží
reset
:
services:
application.application:
create: MyApplication
alteration: true
reset:
- arguments
- setup
- tags
Pokud chcete odstranit službu přidanou rozšířením, můžete to udělat takto:
services:
cache.journal: false