Конфигурация DI-контейнера
Обзор параметров конфигурации DI-контейнера Nette.
Конфигурационный файл
DI-контейнером Nette легко управлять с помощью конфигурационных файлов. Обычно они пишутся в формате NEON. Мы рекомендуем использовать редакторы с поддержкой этого формата.
decorator: 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%.
Если вашему коду (например, классу) нужно значение параметра, передайте его в класс. Например, в конструкторе. Никакого глобального объекта конфигурации, у которого классы могли бы запросить значения параметров, нет. Это было бы нарушением принципа внедрения зависимостей.
Сервисы
См. отдельную главу.
Decorator
Как изменить сразу несколько сервисов определённого типа? Например, как вызвать определённый метод у всех презентеров, наследующих от конкретного базового класса? Для этого и служит decorator.
decorator:
# для всех сервисов, которые являются экземплярами этого класса или интерфейса
App\Presentation\BasePresenter:
setup:
- setProjectId(10) # вызвать этот метод
- $absoluteUrls = true # и задать переменную
Decorator можно использовать и для установки тегов или включения режима inject.
decorator:
InjectableInterface:
tags: [mytag: 1]
inject: true
DI
Технические настройки DI-контейнера.
di:
# показывать DIC в панели Tracy?
debugger: ... # (bool) по умолчанию определяется автоматически (включено при наличии Tracy)
# типы параметров, которые никогда не проходят autowiring
excluded: ... # (string[])
# включить ленивое создание сервисов?
lazy: ... # (bool) по умолчанию false
# класс, от которого наследуется DI-контейнер
parentClass: ... # (string) по умолчанию Nette\DI\Container
Ленивые сервисы
Установка lazy: true включает ленивое (отложенное) создание
сервисов. Это значит, что сервисы на самом деле создаются не в момент
запроса из DI-контейнера, а только при первом использовании. Это может
ускорить старт приложения и снизить расход памяти, потому что
создаются только те сервисы, которые действительно нужны для данного
запроса.
Для конкретного сервиса ленивое создание можно настроить отдельно.
Ленивые объекты можно использовать только для пользовательских классов, но не для внутренних классов PHP. Требуется PHP 8.4 или новее.
Экспорт метаданных
Класс DI-контейнера содержит также много метаданных. Вы можете уменьшить его размер, сократив экспорт метаданных.
di:
export:
# экспортировать параметры?
parameters: false # (bool) по умолчанию true
# экспортировать теги и какие?
tags: # (string[]|bool) по умолчанию все
- event.subscriber
# экспортировать данные для autowiring и какие?
types: # (string[]|bool) по умолчанию все
- Nette\Database\Connection
- Symfony\Component\Console\Application
Если вы не используете $container->getParameters(), вы можете отключить
экспорт параметров. Кроме того, вы можете экспортировать только те
теги, которые действительно используете для получения сервисов через
$container->findByTag(...). Если вы этот метод вообще не вызываете, экспорт
тегов можно полностью отключить значением false.
Метаданные для autowiring можно
заметно сократить, перечислив только те классы, которые вы
действительно запрашиваете через $container->getByType(). Снова: если вы
этот метод не вызываете (или вызываете только в файле bootstrap, например чтобы получить
Nette\Application\Application), экспорт типов можно полностью отключить
значением false.
Extensions
Регистрация дополнительных DI-расширений. Вот так вы добавите,
например, DI-расширение Dibi\Bridges\Nette\DibiExtension3 под именем
dibi:
extensions:
dibi: Dibi\Bridges\Nette\DibiExtension3
Затем вы настраиваете его в секции 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,
имеет более высокий приоритет, чем подключённые в нём файлы.
Search
Автоматическая регистрация сервисов в DI-контейнере значительно упрощает разработку. Nette автоматически добавляет в контейнер презентеры, но вы легко можете добавить и любые другие классы.
Достаточно указать, в каких каталогах (и подкаталогах) искать классы:
search:
- in: %appDir%/Forms
- in: %appDir%/Model
Если вам нужно всего одно правило поиска, список можно опустить и
записать его ключи прямо под search:
search:
in: %appDir%
Однако обычно мы не хотим добавлять совершенно все классы и интерфейсы, поэтому их можно отфильтровать:
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: ...
Помимо классов поиск регистрирует и интерфейсы с единственным
методом create() или get() – как генерируемые фабрики или
аксессоры. Классы, для которых сервис того же типа уже
зарегистрирован в контейнере, пропускаются, поэтому дубликаты не
возникают.
Слияние
Если в нескольких конфигурационных файлах встречаются элементы с одинаковыми ключами, они будут перезаписаны или, в случае массивов, объединены. Подключённый позже файл имеет более высокий приоритет, чем предыдущий.
| config1.neon | config2.neon | результат |
|---|---|---|
|
|
|
Для массивов слияние можно предотвратить, добавив после имени ключа восклицательный знак:
| config1.neon | config2.neon | результат |
|---|---|---|
|
|
|