Definiowanie usług

Konfiguracja to miejsce, w którym instruujemy kontener DI, jak składać poszczególne usługi i jak łączyć je z innymi zależnościami. Nette zapewnia bardzo przejrzysty i elegancki sposób na osiągnięcie tego celu.

Sekcja services w pliku konfiguracyjnym NEON to miejsce, w którym definiujemy nasze niestandardowe usługi i ich konfiguracje. Przyjrzyjmy się prostemu przykładowi definiowania usługi o nazwie database, która reprezentuje instancję klasy PDO:

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

Ta konfiguracja skutkuje następującą metodą fabryczną w kontenerze DI:

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

Nazwy usług pozwalają nam odwoływać się do nich w innych częściach pliku konfiguracyjnego, używając formatu @serviceName. Jeśli nie ma potrzeby nazywania usługi, możemy po prostu użyć punktora:

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

Aby pobrać usługę z kontenera DI, możemy użyć metody getService() z nazwą usługi jako parametrem lub metody getByType() z typem usługi:

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

Tworzenie usług

Najczęściej tworzymy usługę po prostu poprzez instancjonowanie określonej klasy. Na przykład:

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

Jeśli musimy rozszerzyć konfigurację o dodatkowe klucze, definicję można rozszerzyć na wiele linii:

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

Klucz create ma alias factory, obie wersje są powszechne w praktyce. Zalecamy jednak używanie create.

Argumenty konstruktora lub metoda tworzenia mogą być alternatywnie zapisane w kluczu arguments:

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

Usługi nie muszą być tworzone tylko przez prostą instancję klasy; mogą one również wynikać z wywoływania metod statycznych lub metod innych usług:

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

Zauważ, że dla uproszczenia, zamiast ->, używamy ::, patrz wyrażenie oznacza. Te metody fabryczne są generowane:

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

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

Kontener DI musi znać typ tworzonej usługi. Jeśli tworzymy usługę przy użyciu metody, która nie ma określonego typu zwracanego, musimy wyraźnie wspomnieć o tym typie w konfiguracji:

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

Argumenty

Przekazujemy argumenty do konstruktorów i metod w sposób bardzo podobny do zwykłego PHP:

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

Dla lepszej czytelności możemy wypisać argumenty w osobnych wierszach. W tym formacie użycie przecinków jest opcjonalne:

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

Można również nazwać argumenty, co pozwala nie martwić się o ich kolejność:

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

Jeśli chcesz pominąć niektóre argumenty i użyć ich wartości domyślnych lub wstawić usługę poprzez autowiring, użyj podkreślenia:

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

Argumentami mogą być usługi, parametry i wiele innych, patrz środki wyrazu.

Konfiguracja

W sekcji setup definiujemy metody, które powinny być wywoływane podczas tworzenia usługi.

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

W PHP wyglądałoby to następująco:

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

Oprócz wywoływania metod, można również przekazywać wartości do właściwości. Dodawanie elementu do tablicy jest również obsługiwane, ale należy je ująć w cudzysłów, aby uniknąć kolizji ze składnią NEON:

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

W PHP tłumaczyłoby się to na:

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

W konfiguracji można również wywoływać metody statyczne lub metody innych usług. Jeśli chcesz przekazać bieżącą usługę jako argument, użyj @self:

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

Zauważ, że dla uproszczenia, zamiast ->, używamy ::, patrz środki wyrazu. Spowoduje to wygenerowanie następującej metody fabrycznej:

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

Środki wyrazu

Nette DI zapewnia nam wyjątkowo bogate możliwości wyrażania, pozwalając nam wyrazić prawie wszystko. W plikach konfiguracyjnych możemy używać parametrów:

# parametr
%wwwDir%

# wartość pod kluczem parametru
%mailer.user%

# parametr w ciągu znaków
'%wwwDir%/images'

Możemy również tworzyć obiekty, wywoływać metody i funkcje:

# utworzyć obiekt
DateTime()

# wywołać metodę statyczną
Collator::create(%locale%)

# wywołać funkcję PHP
::getenv(DB_USER)

Odnosić się do usług poprzez ich nazwę lub typ:

# usługa według nazwy
@database

# usługa według typu
@Nette\Database\Connection

Używaj składni wywoływalnej pierwszej klasy:

# creating a callback, equivalent to [@user, logout]
@user::logout(...)

Użyj stałych:

# stała klasy
FilesystemIterator::SKIP_DOTS

# stała globalna uzyskana przez funkcję PHP constant()
::constant(PHP_VERSION)

Wywołania metod mogą być łańcuchowane, tak jak w PHP. Dla uproszczenia, zamiast ->, używamy :::

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

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

Wyrażenia te mogą być używane w dowolnym miejscu podczas tworzenia usług, w argumentach, w sekcji konfiguracji lub w parametrach:

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

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

Funkcje specjalne

W plikach konfiguracyjnych można korzystać z tych funkcji specjalnych:

  • not() dla negacji wartości
  • bool(), int(), float(), string() do bezstratnego rzutowania typów
  • typed() do generowania tablicy wszystkich usług określonego typu
  • tagged() do tworzenia tablicy wszystkich usług z danym tagiem
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

W porównaniu do konwencjonalnego rzutowania typów w PHP, takiego jak (int), bezstratne rzutowanie typów rzuci wyjątek dla wartości nienumerycznych.

Funkcja typed() tworzy tablicę wszystkich usług określonego typu (klasy lub interfejsu). Wyklucza ona usługi z wyłączonym autowiringiem. Można określić wiele typów, oddzielając je przecinkami.

services:
	- BarsDependent( typed(Bar) )

Można również automatycznie przekazać tablicę usług określonego typu jako argument przy użyciu autowiring.

Funkcja tagged() tworzy tablicę wszystkich usług z określonym tagiem. Można wymienić wiele tagów, oddzielając je przecinkami.

services:
	- LoggersDependent( tagged(logger) )

Okablowanie

Klucz autowired pozwala modyfikować zachowanie autoprzewodowania dla określonej usługi. Więcej szczegółów można znaleźć w rozdziale autowiring.

services:
	foo:
		create: Foo
		autowired: false     # usługa foo jest wyłączona z automatycznego okablowania

Tagi

Tagi służą do dodawania dodatkowych informacji do usług. Do usługi można przypisać jeden lub więcej tagów:

services:
	foo:
		create: Foo
		tags:
			- cached

Tagi mogą również zawierać wartości:

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

Aby pobrać wszystkie usługi z określonymi tagami, można użyć funkcji tagged():

services:
	- LoggersDependent( tagged(logger) )

W kontenerze DI można uzyskać nazwy wszystkich usług z określonym tagiem za pomocą metody findByTag():

$names = $container->findByTag('logger');
// $names to tablica zawierająca nazwę usługi i wartość tagu
// np. ['foo' => 'monolog.logger.event', ...].

Tryb wstrzykiwania

Użycie flagi inject: true aktywuje przekazywanie zależności poprzez zmienne publiczne z adnotacją inject i metodami inject*().

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

Domyślnie flaga inject jest aktywowana tylko dla prezenterów.

Modyfikacje usługi

Kontener DI zawiera wiele usług dodanych przez rozszerzenia wbudowane lub użytkownika. Definicje tych usług można modyfikować bezpośrednio w konfiguracji. Na przykład można zmienić klasę usługi application.application, która jest konwencjonalnie Nette\Application\Application, na coś innego:

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

Flaga alteration ma charakter informacyjny, wskazując, że jedynie modyfikujemy istniejącą usługę.

Możemy również uzupełnić konfigurację:

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

Podczas nadpisywania usługi może być konieczne usunięcie oryginalnych argumentów, elementów konfiguracji lub tagów, co jest przydatne w przypadku reset:

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

Jeśli chcesz usunąć usługę dodaną przez rozszerzenie, możesz to zrobić w następujący sposób:

services:
	cache.journal: false
wersja: 3.x