Конфігурація DI-контейнера

Огляд конфігураційних опцій для Nette DI-контейнера.

Конфігураційний файл

Nette DI-контейнер легко керується за допомогою конфігураційних файлів. Вони зазвичай записуються у форматі NEON. Для редагування рекомендуємо редактори з підтримкою цього формату.

 decorator: 	Декоратор
di: DI-контейнер
extensions: Встановлення додаткових DI-розширень
includes: Включення файлів
parameters: Параметри
search: Автоматична реєстрація сервісів
services: Сервіси

Щоб записати рядок, що містить символ %, потрібно його екранувати, подвоївши до %%.

Параметри

У конфігурації можна визначити параметри, які потім можна використовувати як частину визначень сервісів. Це може зробити конфігурацію більш зрозумілою або об'єднати та виділити значення, які будуть змінюватися.

parameters:
	dsn: 'mysql:host=127.0.0.1;dbname=test'
	user: root
	password: secret

На параметр dsn можна посилатися будь-де в конфігурації записом %dsn%. Параметри можна використовувати і всередині рядків, як '%wwwDir%/images'.

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

parameters:
	mailer:
		host: smtp.example.com
		secure: ssl
		user: franta@gmail.com
	languages: [cs, en, de]

На конкретний ключ можна посилатися як %mailer.user%.

Якщо вам потрібно у вашому коді, наприклад, у класі, дізнатися значення будь-якого параметра, передайте його до цього класу. Наприклад, у конструкторі. Не існує жодного глобального об'єкта, що представляє конфігурацію, до якого класи могли б звертатися за значеннями параметрів. Це було б порушенням принципу dependency injection.

Сервіси

Див. окремий розділ.

Decorator

Як масово змінити всі сервіси певного типу? Наприклад, викликати певний метод у всіх presenter'ів, які успадковують від конкретного спільного предка? Для цього існує decorator.

decorator:
	# для всіх сервісів, що є екземплярами цього класу або інтерфейсу
	App\Presentation\BasePresenter:
		setup:
			- setProjectId(10)       # виклич цей метод
			- $absoluteUrls = true   # і встанови змінну

Decorator можна також використовувати для налаштування тегів або ввімкнення режиму inject.

decorator:
	InjectableInterface:
		tags: [mytag: 1]
		inject: true

DI

Технічні налаштування DI-контейнера.

di:
	# показати DI-контейнер у Tracy Bar?
	debugger: ...        # (bool) за замовчуванням true

	# типи параметрів, які ніколи не підключати автоматично
	excluded: ...        # (string[])

	# дозволити ліниве створення сервісів?
	lazy: ...            # (bool) за замовчуванням false

	# клас, від якого успадковується DI-контейнер
	parentClass: ...     # (string) за замовчуванням Nette\DI\Container

Lazy-сервіси

Налаштування lazy: true активує ліниве (відкладене) створення сервісів. Це означає, що сервіси не створюються насправді в момент, коли ми їх запитуємо з DI-контейнера, а лише в момент їх першого використання. Це може прискорити запуск програми та зменшити споживання пам'яті, оскільки створюються лише ті сервіси, які дійсно потрібні в даному запиті.

Для конкретного сервісу ліниве створення можна змінити.

Ліниві об'єкти можна використовувати лише для користувацьких класів, а не для внутрішніх класів PHP. Потребує PHP 8.4 або новішої версії.

Експорт метаданих

Клас DI-контейнера містить також багато метаданих. Ви можете зменшити його розмір, скоротивши експорт метаданих.

di:
	export:
		# експортувати параметри?
		parameters: false   # (bool) за замовчуванням true

		# експортувати теги і які?
		tags:               # (string[]|bool) за замовчуванням всі
			- event.subscriber

		# експортувати дані для автопідключення і які?
		types:              # (string[]|bool) за замовчуванням всі
			- Nette\Database\Connection
			- Symfony\Component\Console\Application

Якщо ви не використовуєте масив $container->getParameters(), ви можете вимкнути експорт параметрів. Далі, ви можете експортувати лише ті теги, через які ви отримуєте сервіси методом $container->findByTag(...). Якщо ви взагалі не викликаєте цей метод, ви можете повністю вимкнути експорт тегів за допомогою false.

Ви можете значно скоротити метадані для автоматичного підключення, вказавши класи, які ви використовуєте як параметр методу $container->getByType(). І знову ж таки, якщо ви взагалі не викликаєте цей метод (або лише в bootstrap для отримання Nette\Application\Application), ви можете повністю вимкнути експорт за допомогою false.

Розширення

Реєстрація додаткових DI-розширень. Таким чином додамо, наприклад, DI-розширення Dibi\Bridges\Nette\DibiExtension22 під назвою dibi.

extensions:
	dibi: Dibi\Bridges\Nette\DibiExtension22

Потім ми конфігуруємо його в секції dibi:

dibi:
	host: localhost

Як розширення можна додати і клас, який має параметри:

extensions:
	application: Nette\Bridges\ApplicationDI\ApplicationExtension(%debugMode%, %appDir%, %tempDir%/cache)

Включення файлів

Додаткові конфігураційні файли можна включити в секції includes:

includes:
	- parameters.php
	- services.neon
	- presenters.neon

Назва parameters.php не є помилкою, конфігурація може бути записана також у PHP-файлі, який поверне її як масив:

<?php
return [
	'database' => [
		'main' => [
			'dsn' => 'sqlite::memory:',
		],
	],
];

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

Автоматичне додавання сервісів до DI-контейнера надзвичайно полегшує роботу. Nette автоматично додає до контейнера presenter'и, але можна легко додавати й будь-які інші класи.

Достатньо вказати, у яких каталогах (та підкаталогах) слід шукати класи:

search:
	-	in: %appDir%/Forms
	-	in: %appDir%/Model

Зазвичай, однак, ми не хочемо додавати абсолютно всі класи та інтерфейси, тому їх можна фільтрувати:

search:
	-	in: %appDir%/Forms

		# фільтрація за назвою файлу (string|string[])
		files:
			- *Factory.php

		# фільтрація за назвою класу (string|string[])
		classes:
			- *Factory

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

search:
	-	in: %appDir%
		extends:
			- App\*Form
		implements:
			- App\*FormInterface

Можна визначити і правила виключення, тобто маски назви класу або предків, які, якщо відповідають, сервіс не додається до DI-контейнера:

search:
	-	in: %appDir%
		exclude:
			files: ...
			classes: ...
			extends: ...
			implements: ...

Усім сервісам можна встановити теги:

search:
	-	in: %appDir%
		tags: ...

Об'єднання

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

config1.neon config2.neon результат
items:
	- 1
	- 2
items:
	- 3
items:
	- 1
	- 2
	- 3

Для масивів можна запобігти об'єднанню, вказавши знак оклику після назви ключа:

config1.neon config2.neon результат
items:
	- 1
	- 2
items!:
	- 3
items:
	- 3
версія: 3.x