Definiranje storitev

Konfiguracija je mesto, kjer učimo DI vsebnik, kako naj sestavlja posamezne storitve in kako jih povezuje z drugimi odvisnostmi. Nette ponuja zelo pregleden in eleganten način, kako to doseči.

Sekcija services v konfiguracijski datoteki formata NEON je mesto, kjer definiramo lastne storitve in njihove konfiguracije. Poglejmo si preprost primer definicije storitve, imenovane database, ki predstavlja instanco razreda PDO:

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

Navedena konfiguracija bo vodila do naslednje tovarne metode v DI vsebniku:

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

Imena storitev nam omogočajo, da se nanje sklicujemo v drugih delih konfiguracijske datoteke, in sicer v formatu @imeStoritve. Če storitve ni treba poimenovati, lahko preprosto uporabimo le alinejo:

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

Za pridobitev storitve iz DI vsebnika lahko uporabimo metodo getService() z imenom storitve kot parametrom ali metodo getByType() s tipom storitve:

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

Ustvarjanje storitve

Večinoma ustvarimo storitev preprosto tako, da ustvarimo instanco določenega razreda. Na primer:

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

Če moramo konfiguracijo razširiti z dodatnimi ključi, lahko definicijo razpišemo v več vrstic:

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

Ključ create ima alias factory, obe varianti sta v praksi pogosti. Vendar priporočamo uporabo create.

Argumenti konstruktorja ali ustvarjalne metode so lahko alternativno zapisani v ključu arguments:

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

Storitve ni treba ustvarjati le s preprostim ustvarjanjem instance razreda, lahko so tudi rezultat klica statičnih metod ali metod drugih storitev:

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

Opazite, da se za enostavnost namesto -> uporablja ::, glej Izrazna sredstva. Generirale se bodo te tovarne metode:

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

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

DI vsebnik mora poznati tip ustvarjene storitve. Če ustvarjamo storitev s pomočjo metode, ki nima specificiranega vrnjenega tipa, moramo ta tip eksplicitno navesti v konfiguraciji:

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

Argumenti

V konstruktor in metode predajamo argumente na način, ki je zelo podoben kot v samem PHP:

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

Za boljšo berljivost lahko argumente razpišemo v ločene vrstice. V takem primeru je uporaba vejic neobvezna:

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

Argumente lahko tudi poimenujete in vam ni treba skrbeti za njihov vrstni red:

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

Če želite nekatere argumente izpustiti in uporabiti njihovo privzeto vrednost ali dodati storitev s pomočjo autowiringa, uporabite podčrtaj:

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

Kot argumente lahko predajate storitve, uporabljate parametre in še veliko več, glej Izrazna sredstva.

Setup

V sekciji setup definiramo metode, ki se morajo poklicati pri ustvarjanju storitve.

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

To bi v PHP izgledalo takole:

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

Poleg klicanja metod lahko tudi predajate vrednosti v lastnosti. Podprto je tudi dodajanje elementa v polje, ki ga je treba zapisati v narekovajih, da ne pride do kolizije s sintakso NEON:

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

Kar bi v PHP kodi izgledalo takole:

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

V setupu lahko pa kličete tudi statične metode ali metode drugih storitev. Če morate kot argument predati trenutno storitev, jo navedite kot @self:

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

Opazite, da se za enostavnost namesto -> uporablja ::, glej Izrazna sredstva. Generirala se bo takšna tovarna metoda:

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

Izrazna sredstva

Nette DI nam daje izjemno bogata izrazna sredstva, s katerimi lahko zapišemo skoraj karkoli. V konfiguracijskih datotekah lahko tako uporabljamo parametre:

# parameter
%wwwDir%

# vrednost parametra pod ključem
%mailer.user%

# parameter znotraj niza
'%wwwDir%/images'

Nadalje ustvarjati objekte, klicati metode in funkcije:

# ustvarjanje objekta
DateTime()

# klic statične metode
Collator::create(%locale%)

# klic PHP funkcije
::getenv(DB_USER)

Sklicujemo se na storitve bodisi po njihovem imenu ali s pomočjo tipa:

# storitev po imenu
@database

# storitev po tipu
@Nette\Database\Connection

Uporabljati first-class callable sintakso:

# ustvarjanje povratnega klica, podobno [@user, logout]
@user::logout(...)

Uporabljati konstante:

# konstanta razreda
FilesystemIterator::SKIP_DOTS

# globalno konstanto dobimo s PHP funkcijo constant()
::constant(PHP_VERSION)

Klicanje metod lahko verižimo enako kot v PHP. Le za enostavnost se namesto -> uporablja :::

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

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

Te izraze lahko uporabljate kjerkoli, pri ustvarjanju storitev, v argumentih, v sekciji setup ali parametrih:

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

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

Posebne funkcije

V konfiguracijskih datotekah lahko uporabljate te posebne funkcije:

  • not() negacija vrednosti
  • bool(), int(), float(), string() pretvorba brez izgube v dani tip
  • typed() ustvari polje vseh storitev specificiranega tipa
  • tagged() ustvari polje vseh storitev z dano oznako
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

V primerjavi s klasično pretvorbo v PHP, kot je npr. (int), pretvorba brez izgube vrže izjemo za neštevilske vrednosti.

Funkcija typed() ustvari polje vseh storitev danega tipa (razred ali vmesnik). Izpusti storitve, ki imajo izklopljen autowiring. Lahko navedete tudi več tipov, ločenih z vejico.

services:
	- BarsDependent( typed(Bar) )

Polje storitev določenega tipa lahko predajate kot argument tudi samodejno s pomočjo autowiringa.

Funkcija tagged() pa ustvarja polje vseh storitev z določeno oznako. Tudi tukaj lahko specificirate več oznak, ločenih z vejico.

services:
	- LoggersDependent( tagged(logger) )

Autowiring

Ključ autowired omogoča vplivanje na obnašanje autowiringa za specifično storitev. Za podrobnosti glej poglavje o autowiringu.

services:
	foo:
		create: Foo
		autowired: false     # storitev foo je izključena iz autowiringa

Lazy storitve

Lazy loading je tehnika, ki odloži ustvarjanje storitve do trenutka, ko je dejansko potrebna. V globalni konfiguraciji lahko omogočite lazy ustvarjanje za vse storitve hkrati. Za posamezne storitve pa lahko to obnašanje prepišete:

services:
	foo:
		create: Foo
		lazy: false

Ko je storitev definirana kot lazy, ob njeni zahtevi iz DI vsebnika dobimo poseben nadomestni objekt. Ta izgleda in se obnaša enako kot dejanska storitev, vendar se dejanska inicializacija (klic konstruktorja in setupa) zgodi šele ob prvem klicu katerekoli njene metode ali lastnosti.

Lazy loading je mogoče uporabiti samo za uporabniške razrede, ne pa za notranje PHP razrede. Zahteva PHP 8.4 ali novejšo različico.

Oznake

Oznake (tags) služijo za dodajanje dopolnilnih informacij k storitvam. Storitvi lahko dodate eno ali več oznak:

services:
	foo:
		create: Foo
		tags:
			- cached

Oznake lahko nosijo tudi vrednosti:

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

Da bi dobili vse storitve z določenimi oznakami, lahko uporabite funkcijo tagged():

services:
	- LoggersDependent( tagged(logger) )

V DI vsebniku lahko dobite imena vseh storitev z določeno oznako s pomočjo metode findByTag():

$names = $container->findByTag('logger');
// $names je polje, ki vsebuje ime storitve in vrednost oznake
// npr. ['foo' => 'monolog.logger.event', ...]

Način Inject

S pomočjo zastavice inject: true se aktivira predajanje odvisnosti preko javnih spremenljivk z anotacijo inject in metod inject*().

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

Privzeto je inject aktiviran samo za presenterje.

Modifikacija storitev

DI vsebnik vsebuje veliko storitev, ki so bile dodane preko vgrajene ali uporabniške razširitve. Definicije teh storitev lahko prilagodite neposredno v konfiguraciji. Na primer, lahko spremenite razred storitve application.application, ki je standardno Nette\Application\Application, na drugega:

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

Zastavica alteration je informativna in pove, da le modificiramo obstoječo storitev.

Lahko tudi dopolnimo setup:

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

Pri prepisovanju storitve lahko želimo odstraniti prvotne argumente, postavke setupa ali oznake, za kar služi reset:

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

Če želite odstraniti storitev, dodano z razširitvijo, lahko to storite takole:

services:
	cache.journal: false
različica: 3.x