Nette Documentation Preview

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

.[perex]
Конфігурація - це місце, де ми вказуємо DI контейнеру, як зібрати окремі сервіси і як з'єднати їх з іншими залежностями. Nette надає дуже чіткий та елегантний спосіб досягти цього.

Секція `services` у файлі конфігурації NEON - це місце, де ми визначаємо наші кастомні сервіси та їхні конфігурації. Давайте розглянемо простий приклад визначення сервісу з ім'ям `database`, який представляє екземпляр класу `PDO`:

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

Ця конфігурація призводить до наступного заводського методу в контейнері [DI |container]:

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

Назви сервісів дозволяють нам посилатися на них в інших частинах конфігураційного файлу, використовуючи формат `@serviceName`. Якщо немає необхідності називати службу, ми можемо просто використовувати маркер:

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

Щоб отримати сервіс з контейнера DI, ми можемо використовувати метод `getService()` з назвою сервісу як параметром або метод `getByType()` з типом сервісу:

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


Створення сервісу .[#toc-service-creation]
==========================================

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

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

Якщо нам потрібно розширити конфігурацію за допомогою додаткових ключів, визначення можна розгорнути на кілька рядків:

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

Ключ `create` має псевдонім `factory`, обидві версії поширені на практиці. Однак ми рекомендуємо використовувати `create`.

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

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

Сервіси не обов'язково створювати простим екземпляром класу; вони також можуть бути результатом виклику статичних методів або методів інших сервісів:

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

Зауважте, що для простоти замість `->` ми використовуємо `::`, див. [засоби вираження |#expression means]. Ці фабричні методи генеруються:

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

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

Контейнер DI повинен знати тип створюваного сервісу. Якщо ми створюємо сервіс за допомогою методу, який не має визначеного типу повернення, ми повинні явно вказати цей тип у конфігурації:

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


Аргументи .[#toc-arguments]
===========================

Ми передаємо аргументи конструкторам і методам у спосіб, дуже схожий на звичайний PHP:

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

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

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

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

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

Якщо ви хочете пропустити певні аргументи і використовувати їхні значення за замовчуванням або вставити сервіс за допомогою [автопідключення |autowiring], використовуйте символ підкреслення:

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

Аргументами можуть бути сервіси, параметри та багато іншого, див. розділ [Засоби вираження |#expression means].


Налаштування .[#toc-setup]
==========================

У розділі `setup` ми визначаємо методи, які слід викликати при створенні сервісу.

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

На PHP це буде виглядати так:

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

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

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

У PHP це матиме вигляд:

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

У налаштуванні ви також можете викликати статичні методи або методи інших сервісів. Якщо вам потрібно передати поточний сервіс як аргумент, використовуйте `@self`:

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

Зауважте, що для простоти замість `->` ми використовуємо `::`, див. [засоби вираження |#expression means]. Це генерує наступний заводський метод:

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


Засоби вираження .[#toc-expression-means]
=========================================

Nette DI надає нам надзвичайно багаті можливості для вираження, що дозволяє сформулювати майже все, що завгодно. У конфігураційних файлах ми можемо використовувати [параметри |configuration#parameters]:

```neon
# параметр
%wwwDir%

# значення під ключем параметра
%mailer.user%

# параметр у рядку
'%wwwDir%/images'
```

Ми також можемо створювати об'єкти, викликати методи та функції:

```neon
# створити об'єкт
DateTime()

# викликати статичний метод
Collator::create(%locale%)

# виклик функції PHP
::getenv(DB_USER)
```

Звертайтеся до сервісів за назвою або за типом:

```neon
# послуга за назвою
@database

# послуга за типом
@Nette\Database\Connection
```

Використовуйте першокласний синтаксис викликів: .{data-version:3.2.0}

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

Використовуйте константи:

```neon
# константа класу
FilesystemIterator::SKIP_DOTS

# глобальна константа, отримана функцією PHP constant()
::constant(PHP_VERSION)
```

Виклики методів можна об'єднувати в ланцюжок, так само як і в PHP. Для простоти, замість `->` ми використовуємо `::`:

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

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

Ці вирази можна використовувати будь-де при [створенні сервісів |#Service Creation], в [аргументах |#Arguments], в розділі [налаштувань |#setup] або [параметрах |configuration#parameters]:

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

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


Спеціальні функції .[#toc-special-functions]
--------------------------------------------

У файлах конфігурації ви можете використовувати ці спеціальні функції:

- `not()` для заперечення значення
- `bool()`, `int()`, `float()`, `string()` для приведення типів без втрат
- `typed()` для створення масиву всіх сервісів заданого типу
- `tagged()` для створення масиву всіх сервісів із заданим тегом

```neon
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)
```

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

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

```neon
services:
	- BarsDependent( typed(Bar) )
```

Ви також можете автоматично передати масив сервісів певного типу як аргумент за допомогою [автопідключення |autowiring#Collection of Services].

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

```neon
services:
	- LoggersDependent( tagged(logger) )
```


Автопроводка .[#toc-autowiring]
===============================

За допомогою ключа `autowired` ви можете змінити поведінку автопідключення для певного сервісу. Для більш детальної інформації дивіться [розділ про автопідключення |autowiring].

```neon
services:
	foo:
		create: Foo
		autowired: false     # сервіс foo виключено з автопідключення
```


Ліниві сервіси .[#toc-lazy-services]
====================================

Ліниве завантаження - це техніка, яка відкладає створення сервісу до моменту, коли він дійсно буде потрібен. Ви можете увімкнути [ліниве створення сервісів глобально |configuration#Lazy Services] в конфігурації для всіх сервісів одразу. Для окремих сервісів цю поведінку можна перевизначити:

```neon
services:
	foo:
		create: Foo
		lazy: false
```

Коли сервіс визначено як лінивий, запит до нього з контейнера DI повертатиме спеціальний проксі-об'єкт. Цей проксі виглядає і поводиться як справжній сервіс, але справжня ініціалізація (виклик конструктора і налаштування) відбудеться лише при першому виклику будь-якого з його методів або властивостей.

.[note]
Ліниве завантаження можна використовувати лише для класів, визначених користувачем, але не для внутрішніх класів PHP. Потрібна версія PHP 8.4 або новіша.


Теги .[#toc-tags]
=================

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

```neon
services:
	foo:
		create: Foo
		tags:
			- cached
```

Мітки також можуть мати значення:

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

Щоб отримати всі сервіси з певними тегами, ви можете скористатися функцією `tagged()`:

```neon
services:
	- LoggersDependent( tagged(logger) )
```

У контейнері DI ви можете отримати назви всіх сервісів з певним тегом за допомогою методу `findByTag()`:

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


Режим впорскування .[#toc-inject-mode]
======================================

Використання прапора `inject: true` активує передачу залежностей через загальнодоступні змінні з анотацією [inject |best-practices:inject-method-attribute#Inject Attributes] та методами [inject*( |best-practices:inject-method-attribute#inject Methods] ).

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

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


Модифікації сервісу .[#toc-service-modifications]
=================================================

Контейнер DI містить багато сервісів, доданих за допомогою вбудованих або [користувацьких розширень |#extensions]. Ви можете змінювати визначення цих сервісів безпосередньо в конфігурації. Наприклад, ви можете змінити клас сервісу `application.application`, який умовно називається `Nette\Application\Application`, на щось інше:

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

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

Ми також можемо доповнити налаштування:

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

При перезаписуванні сервісу ви можете захотіти видалити оригінальні аргументи, елементи налаштувань або теги, і саме тут у нагоді стане `reset`:

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

Якщо ви хочете видалити сервіс, доданий розширенням, ви можете зробити це так:

```neon
services:
	cache.journal: false
```

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

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

Секція services у файлі конфігурації NEON – це місце, де ми визначаємо наші кастомні сервіси та їхні конфігурації. Давайте розглянемо простий приклад визначення сервісу з ім'ям database, який представляє екземпляр класу PDO:

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

Ця конфігурація призводить до наступного заводського методу в контейнері DI:

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

Назви сервісів дозволяють нам посилатися на них в інших частинах конфігураційного файлу, використовуючи формат @serviceName. Якщо немає необхідності називати службу, ми можемо просто використовувати маркер:

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

Щоб отримати сервіс з контейнера 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)

Якщо нам потрібно розширити конфігурацію за допомогою додаткових ключів, визначення можна розгорнути на кілька рядків:

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

Ключ create має псевдонім factory, обидві версії поширені на практиці. Однак ми рекомендуємо використовувати create.

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

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

Сервіси не обов'язково створювати простим екземпляром класу; вони також можуть бути результатом виклику статичних методів або методів інших сервісів:

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

Зауважте, що для простоти замість -> ми використовуємо ::, див. засоби вираження. Ці фабричні методи генеруються:

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

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

Контейнер DI повинен знати тип створюваного сервісу. Якщо ми створюємо сервіс за допомогою методу, який не має визначеного типу повернення, ми повинні явно вказати цей тип у конфігурації:

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

Аргументи

Ми передаємо аргументи конструкторам і методам у спосіб, дуже схожий на звичайний PHP:

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

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

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

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

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

Якщо ви хочете пропустити певні аргументи і використовувати їхні значення за замовчуванням або вставити сервіс за допомогою автопідключення, використовуйте символ підкреслення:

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

Аргументами можуть бути сервіси, параметри та багато іншого, див. розділ Засоби вираження.

Налаштування

У розділі 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)

Зауважте, що для простоти замість -> ми використовуємо ::, див. засоби вираження. Це генерує наступний заводський метод:

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

Засоби вираження

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

# параметр
%wwwDir%

# значення під ключем параметра
%mailer.user%

# параметр у рядку
'%wwwDir%/images'

Ми також можемо створювати об'єкти, викликати методи та функції:

# створити об'єкт
DateTime()

# викликати статичний метод
Collator::create(%locale%)

# виклик функції PHP
::getenv(DB_USER)

Звертайтеся до сервісів за назвою або за типом:

# послуга за назвою
@database

# послуга за типом
@Nette\Database\Connection

Використовуйте першокласний синтаксис викликів:

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

Використовуйте константи:

# константа класу
FilesystemIterator::SKIP_DOTS

# глобальна константа, отримана функцією PHP constant()
::constant(PHP_VERSION)

Виклики методів можна об'єднувати в ланцюжок, так само як і в PHP. Для простоти, замість -> ми використовуємо :::

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

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

Ці вирази можна використовувати будь-де при створенні сервісів, в аргументах, в розділі налаштувань або параметрах:

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

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

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

У файлах конфігурації ви можете використовувати ці спеціальні функції:

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

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

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

services:
	- BarsDependent( typed(Bar) )

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

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

services:
	- LoggersDependent( tagged(logger) )

Автопроводка

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

services:
	foo:
		create: Foo
		autowired: false     # сервіс foo виключено з автопідключення

Ліниві сервіси

Ліниве завантаження – це техніка, яка відкладає створення сервісу до моменту, коли він дійсно буде потрібен. Ви можете увімкнути ліниве створення сервісів глобально в конфігурації для всіх сервісів одразу. Для окремих сервісів цю поведінку можна перевизначити:

services:
	foo:
		create: Foo
		lazy: false

Коли сервіс визначено як лінивий, запит до нього з контейнера DI повертатиме спеціальний проксі-об'єкт. Цей проксі виглядає і поводиться як справжній сервіс, але справжня ініціалізація (виклик конструктора і налаштування) відбудеться лише при першому виклику будь-якого з його методів або властивостей.

Ліниве завантаження можна використовувати лише для класів, визначених користувачем, але не для внутрішніх класів PHP. Потрібна версія PHP 8.4 або новіша.

Теги

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

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 є інформативним, вказуючи на те, що ми просто змінюємо існуючий сервіс.

Ми також можемо доповнити налаштування:

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

При перезаписуванні сервісу ви можете захотіти видалити оригінальні аргументи, елементи налаштувань або теги, і саме тут у нагоді стане reset:

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

Якщо ви хочете видалити сервіс, доданий розширенням, ви можете зробити це так:

services:
	cache.journal: false