Визначення сервісів

Конфігурація – це місце, де ми розміщуємо визначення користувацьких сервісів. Робиться це в секції services.

Наприклад, ось як ми створюємо сервіс з іменем database, який буде екземпляром класу PDO:

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

Іменування служб використовується для того, щоб ми могли посилатися на них. Якщо на сервіс не посилаються, немає необхідності давати йому ім'я. Тому замість імені ми просто використовуємо двокрапку:

services:
	- PDO('sqlite::memory:') # анонімний сервіс

Однорядковий запис може бути розбитий на кілька рядків, щоб можна було додати додаткові ключі, наприклад setup. Псевдонім для ключа create: – factory:.

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

Потім ми отримуємо сервіс із контейнера DI, використовуючи метод getService() за ім'ям, або, що ще краще, метод getByType() за типом:

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

Створення сервісу

Найчастіше ми створюємо сервіс, просто створюючи екземпляр класу:

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

Що створить фабричний метод у DI-контейнері:

public function createServiceDatabase(): PDO
{
	return new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'secret');
}

Альтернативно, ключ arguments може бути використаний для передачі аргументів:

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

Статичний метод також може створити сервіс:

services:
	database: My\Database::create(root, secret)

Це еквівалентно коду PHP:

public function createServiceDatabase(): PDO
{
	return My\Database::create('root', 'secret');
}

Передбачається, що статичний метод My\Database::create() має певне значення, що повертається, яке повинен знати контейнер DI. Якщо у нього його немає, ми записуємо тип у конфігурацію:

services:
	database:
		create: My\Database::create(root, secret)
		type: PDO

Nette DI надає вам надзвичайно потужні засоби вираження, що дозволяють написати практично все, що завгодно. Наприклад, щоб звернутися до іншої служби і викликати її метод. Для простоти замість -> використовується ::.

services:
	routerFactory: App\Router\Factory
	router: @routerFactory::create()

Це еквівалентно коду PHP:

public function createServiceRouterFactory(): App\Router\Factory
{
	return new App\Router\Factory;
}

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

Виклики методів можна об'єднувати в ланцюжки, як у PHP:

services:
	foo: FooFactory::build()::get()

Це еквівалентно коду PHP:

public function createServiceFoo()
{
	return FooFactory::build()->get();
}

Аргументи

Іменовані параметри також можуть використовуватися для передачі аргументів:

services:
	database: PDO(
		'mysql:host=127.0.0.1;dbname=test' # позиційний
		username: root                     # іменований
		password: secret                   # іменований
	)

Використання ком необов'язкове при розбитті аргументів на кілька рядків.

Звичайно, ми також можемо використовувати інші сервіси або параметри як аргументи:

services:
	- Foo(@anotherService, %appDir%)

Відповідає коду PHP:

public function createService01(): Foo
{
	return new Foo($this->getService('anotherService'), '...');
}

Якщо перший аргумент – автомонтований, а ви хочете вказати другий, опустіть перший за допомогою символу _, например Foo(_, %appDir%). Або, що ще краще, передавайте тільки другий аргумент як іменований параметр, наприклад: Foo(path: %appDir%).

Nette DI і формат NEON дають вам надзвичайно потужні виразні засоби, що дозволяють написати практично все, що завгодно. Таким чином, аргументом може бути новостворений об'єкт, ви можете викликати статичні методи, методи інших сервісів або навіть глобальні функції, використовуючи спеціальну нотацію:

services:
	analyser: My\Analyser(
		FilesystemIterator(%appDir%)        # створюємо об'єкт
		DateTime::createFromFormat('Y-m-d') # викликаємо статичний метод
		@anotherService                     # передаємо інший сервіс
		@http.request::getRemoteAddress()   # викликаємо метод іншого сервісу
		::getenv(NetteMode)                 # викликаємо глобальну функцію
	)

Відповідає коду PHP:

public function createServiceAnalyser(): My\Analyser
{
	return new My\Analyser(
		new FilesystemIterator('...'),
		DateTime::createFromFormat('Y-m-d'),
		$this->getService('anotherService'),
		$this->getService('http.request')->getRemoteAddress(),
		getenv('NetteMode')
	);
}

Спеціальні функції

Ви також можете використовувати спеціальні функції в аргументах для приведення або заперечення значень:

  • not(%arg%) заперечення
  • bool(%arg%) приведення до bool без втрат
  • int(%arg%) приведення до int без втрат
  • float(%arg%) приведення до плаваючого стану без втрат
  • string(%arg%) приведення до рядка без втрат
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

Переписування без втрат відрізняється від звичайного переписування в PHP, наприклад використовуючи (int), у тому, що воно викидає виняток для нечислових значень.

Як аргументи можна передавати кілька сервісів. Масив усіх сервісів певного типу (тобто класу або інтерфейсу) створюється функцією typed(). Функція буде опускати сервіси, у яких відключено автопідключення, при цьому можна вказати кілька типів, розділених комою.

services:
	- BarsDependent( typed(Bar) )

Ви також можете передати масив сервісів автоматично, використовуючи автозв'язування.

Масив усіх сервісів із певним тегом створюється функцією tagged(). Можна вказати кілька тегів, розділених комою.

services:
	- LoggersDependent( tagged(logger) )

Посилання на сервіси

Посилання на окремі сервіси використовуються за допомогою символу @ и имени, например @database:

services:
	- create: Foo(@database)
	  setup:
			- setCacheStorage(@cache.storage)

Відповідає коду PHP:

public function createService01(): Foo
{
	$service = new Foo($this->getService('database'));
	$service->setCacheStorage($this->getService('cache.storage'));
	return $service;
}

Навіть на анонімні сервіси можна посилатися за допомогою зворотного виклику, просто вкажіть їхній тип (клас або інтерфейс) замість імені. Однак зазвичай у цьому немає потреби через автозв'язування.

services:
	- create: Foo(@Nette\Database\Connection)  # или @\PDO
	  setup:
			- setCacheStorage(@cache.storage)

Setup

У секції setup ми перераховуємо методи, які будуть викликатися під час створення сервісу:

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

Відповідає коду PHP:

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

Також можна встановити властивості. Додавання елемента в масив також підтримується, і його слід писати в лапках, щоб не конфліктувати із синтаксисом NEON:

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

Відповідає коду PHP:

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

Однак статичні методи або методи інших сервісів також можуть бути викликані в налаштуванні. Ми передаємо їм фактичний сервіс як @self:

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

Відповідає коду PHP:

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

Автозв'язування

Ключ autowired можна використовувати для виключення сервісу з автозв'язування або для впливу на його поведінку. Детальнішу інформацію див. у розділі Автозв'язування.

services:
	foo:
		create: Foo
		autowired: false # foo видаляється з автозв'язування

Теги

Інформація про користувача може бути додана до окремих сервісів у вигляді тегів:

services:
	foo:
		create: Foo
		tags:
			- cached

Теги також можуть мати значення:

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

Масив сервісів з певними тегами може бути переданий як аргумент за допомогою функції tagged(). Можна також вказати кілька тегів, розділених комою.

services:
	- LoggersDependent( tagged(logger) )

Імена служб можна отримати з контейнера DI за допомогою методу findByTag():

$names = $container->findByTag('logger');
// $names - масив, що містить ім'я сервісу і значення тега
// наприклад ['foo' => 'monolog.logger.event', ...]

Режим впровадження

Прапор inject: true використовується для активації передавання залежностей через публічні змінні за допомогою анотації inject і методів inject*().

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

За замовчуванням inject активується тільки для презентерів.

Модифікація сервісів

У контейнері DI є низка сервісів, які були додані вбудованим або вашим розширенням. Визначення цих сервісів можуть бути змінені в конфігурації. Наприклад, для сервісу application.application, який за замовчуванням є об'єктом Nette\Application\Application, ми можемо змінити клас:

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

Прапор alteration є інформативним і говорить про те, що ми просто змінюємо існуючий сервіс.

Ми також можемо додати setup:

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

Під час перезапису сервісу ми можемо видалити вихідні аргументи, елементи setup або теги, для яких reset є:

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

Сервіс, доданий розширенням, також може бути видалено з контейнера:

services:
	cache.journal: false
версію: 3.x