Definování služeb
Konfigurace je místem, kam umísťujeme definice vlastních služeb. Slouží k tomu sekce
services
.
Například takto vytvoříme službu pojmenovanou database
, což bude instance třídy PDO
:
services:
database: PDO('sqlite::memory:')
Pojmenování služeb slouží k tomu, abychom se na ně mohli odkazovat. Pokud na službu není odkazováno, není ji potřeba pojmenovávat. Místo názvu tak použijeme jen odrážku:
services:
- PDO('sqlite::memory:') # anonymní služba
Jednořádkový zápis lze rozepsat do více řádků a tak umožnit přidání dalších klíčů, jako je například setup:
services:
database:
factory: PDO('sqlite::memory:')
setup: ...
Službu poté získáme z DI kontejneru metodou getService()
podle názvu, nebo ještě lépe metodou
getByType()
podle typu:
$database = $container->getService('database');
$database = $container->getByType(PDO::class);
Vytvoření služby
Nejčastěji službu vytváříme prostým vytvořením instance určité třídy:
services:
database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
Což vygeneruje tovární metodu v DI kontejneru:
public function createServiceDatabase()
{
return new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'secret');
}
Pro předání argumentů lze alternativně použít i klíč arguments
:
services:
database:
factory: PDO
arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]
Službu může vytvořit také statická metoda:
services:
database: My\Database::create(root, secret)
Odpovídá PHP kódu:
public function createServiceDatabase()
{
return My\Database::create('root', 'secret');
}
Předpokládá se, statická metoda My\Database::create()
má definovanou návratovou hodnotu, buď pomocí
anotace @return
nebo type hintů v PHP 7, kterou DI kontejner potřebuje znát. Pokud ji nemá, zapíšeme typ do
konfigurace:
services:
database:
factory: My\Database::create(root, secret)
type: PDO
Nette DI nám dává mimořádně silné výrazové prostředky, pomocí kterých můžete zapsat téměř cokoliv. Například
se odkázat na jinou službu a zavolat její metodu. Pro jednoduchost se místo
->
používá ::
services:
routerFactory: App\Router\Factory
router: @routerFactory::create()
Odpovídá PHP kódu:
public function createServiceRouterFactory()
{
return new App\Router\Factory;
}
public function createServiceRouter()
{
return $this->getService('routerFactory')->create();
}
Volání metod lze řetězit za sebe stejně jako v PHP:
services:
foo: FooFactory::build()::get()
Odpovídá PHP kódu:
public function createServiceFoo()
{
return FooFactory::build()->get();
}
Argumenty
Pro předání argumentů lze používat i pojmenované parametry:
services:
database: PDO(
'mysql:host=127.0.0.1;dbname=test' # poziční
username: root # pojmenovaný
password: secret # pojmenovaný
)
Při rozepsání argumentů do více řádků je používání čárek volitelné.
Jako argumenty můžeme samozřejmě použít i jiné služby nebo parametry:
services:
- Foo(@anotherService, %appDir%)
Odpovídá PHP kódu:
public function createService01()
{
return new Foo($this->getService('anotherService'), '...');
}
Pokud se má první argument autowirovat a chceme
přitom uvést argument druhý, vynecháme první znakem _
, tedy např. Foo(_, %appDir%)
. Nebo ještě
lépe předáme jen druhý argument jako pojmenovaný parametr, např. Foo(path: %appDir%)
.
Nette DI a formát NEON nám dává mimořádně silné výrazové prostředky, pomocí kterých můžete zapsat téměř cokoliv. Argumentem tak může být nově vytvořený objekt, lze volat statické metody, metody jiných služeb, nebo pomocí speciálního zápisu i globální funkce:
services:
analyser: My\Analyser(
FilesystemIterator(%appDir%) # vytvoření objektu
DateTime::createFromFormat('Y-m-d') # volání statické metody
@anotherService # předání jiné služby
@http.request::getRemoteAddress() # volání metody jiné služby
::getenv(NETTE_MODE) # volání globální funkce
)
Odpovídá PHP kódu:
public function createServiceAnalyser()
{
return new My\Analyser(
new FilesystemIterator('...'),
DateTime::createFromFormat('Y-m-d'),
$this->getService('anotherService'),
$this->getService('http.request')->getRemoteAddress(),
getenv('NETTE_MODE')
);
}
Odkazování na služby
Na jednotlivé služby se odkazuje pomocí zavináče a názvu služby, takže například @database
:
services:
- factory: Foo(@database)
setup:
- setCacheStorage(@cache.storage)
Odpovídá PHP kódu:
public function createService01()
{
$service = new Foo($this->getService('database'));
$service->setCacheStorage($this->getService('cache.storage'));
return $service;
}
I na anonymní služby se lze odkazovat přes zavináč, jen místo názvu uvedeme jejich typ (třídu nebo rozhraní). Tohle ovšem obvykle není potřeba dělat díky autowiringu.
services:
- factory: Foo(@Nette\Database\Connection) # nebo třeba @\PDO
setup:
- setCacheStorage(@cache.storage)
Setup
V sekci setup uvádíme metody, které se mají zavolat při vytváření služby:
services:
database:
factory: PDO(%dsn%, %user%, %password%)
setup:
- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
Odpovídá PHP kódu:
public function createServiceDatabase()
{
$service = new PDO('...', '...', '...');
$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $service;
}
Lze také nastavovat hodnoty proměnných. 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:
factory: Foo
setup:
- $value = 123
- '$onClick[]' = [@bar, clickHandler]
Odpovídá PHP kódu:
public function createServiceFoo()
{
$service = new Foo;
$service->value = 123;
$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
return $service;
}
V setupu lze však volat i statické metody nebo metod jiných služeb. Aktuální službu jim předáme jako
@self
:
services:
foo:
factory: Foo
setup:
- My\Helpers::initializeFoo(@self)
- @anotherService::setFoo(@self)
Odpovídá PHP kódu:
public function createServiceFoo()
{
$service = new Foo;
My\Helpers::initializeFoo($service);
$this->getService('anotherService')->setFoo($service);
return $service;
}
Autowiring
Pomocí klíče autowired lze službu vyřadit z autowiringu nebo jeho chování ovlivnit. Více v kapitole o autowiringu.
services:
foo:
factory: Foo
autowired: false # služba foo je vyřazena z autowiringu
Tagy
Jednotlivým službám lze přidávat uživatelské informace v podobě tzv. tagů:
services:
foo:
factory: Foo
tags:
- cached
Tagy mohou mít i hodnotu:
services:
foo:
factory: Foo
tags:
logger: monolog.logger.event
Názvy služeb lze získat z DI kontejneru metodou 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:
factory: App\Model\Articles
inject: true
V základním nastavení je inject
aktivováno pouze pro presentery.
Modifikace služeb
V DI kontejneru je řada služeb, které přidaly vestavěné nebo vaše rozšíření. Definice
těchto služeb lze v konfiguraci pozměnit. Třeba u služby application.application
, což je standardně objekt
Nette\Application\Application
, můžeme změnit třídu:
services:
application.application:
factory: 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:
factory: 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:
factory: MyApplication
alteration: true
reset:
- arguments
- setup
- tags
Službu přidanou rozšířením lze také z kontejneru odstranit:
services:
cache.journal: false