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-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
		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