Автоматические фабрики
Отдельный класс-фабрика для каждого контроллера и сервиса — много однообразного кода. В пакете laminas-servicemanager есть две готовые фабрики, которые сами подставляют зависимости в конструктор. Устанавливать ничего не нужно — пакет уже есть в проекте.
ReflectionBasedAbstractFactory
Эта фабрика читает типы аргументов конструктора и берёт из контейнера сервисы с такими же именами. Удалите файл PostControllerFactory.php из прошлого урока и измените настройки:
// module/Blog/config/module.config.php
// в начале файла:
// use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory;
'controllers' => [
'factories' => [
Controller\PostController::class => ReflectionBasedAbstractFactory::class,
],
],Конструктор контроллера остался прежним — __construct(private ReadingTime $readingTime). Фабрика увидит тип ReadingTime, найдёт сервис Blog\Service\ReadingTime в контейнере и передаст его. Страница /blog/1 работает как раньше.
Если в конструктор добавить ещё один сервис, настройки менять не придётся.
Что она умеет подставить
| Аргумент конструктора | Что будет передано |
|---|---|
| Тип — класс или интерфейс | Сервис с таким именем из контейнера |
array $config | Весь массив настроек приложения |
| Аргумент со значением по умолчанию | Значение по умолчанию |
| Строка или число без значения по умолчанию | Ошибка: фабрика не знает, что подставить |
ConfigAbstractFactory
Вторая фабрика не изучает класс, а читает список зависимостей из настроек. Список записывают под ключом ConfigAbstractFactory::class:
// module/Blog/config/module.config.php
// в начале файла:
// use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
'controllers' => [
'factories' => [
Controller\PostController::class => ConfigAbstractFactory::class,
],
],
ConfigAbstractFactory::class => [
Controller\PostController::class => [
Service\ReadingTime::class,
],
],В массиве перечислены сервисы в том порядке, в каком их ждёт конструктор. Результат тот же, но зависимости видны прямо в файле настроек.
Что выбрать
| Способ | Плюсы | Минусы |
|---|---|---|
| Свой класс-фабрика | Любая логика, самая быстрая | Много кода |
ReflectionBasedAbstractFactory | Ничего не нужно писать | Чуть медленнее: класс изучается при каждом создании |
ConfigAbstractFactory | Зависимости видны в настройках | Список нужно обновлять вручную |
На протяжении курса контроллеры и простые сервисы регистрируются через ReflectionBasedAbstractFactory. Свою фабрику пишут, когда объекту нужно что-то кроме сервисов: значение из настроек, имя таблицы, путь к файлу.
Генерация фабрик
Для рабочего сервера обычные фабрики можно не писать руками, а сгенерировать. В пакете есть консольная программа:
vendor/bin/generate-factory-for-class "Blog\Controller\PostController" > module/Blog/src/Controller/PostControllerFactory.phpКоманда печатает код фабрики, а знак > сохраняет его в файл. Остаётся указать этот класс в разделе controllers.
Частая ошибка
ReflectionBasedAbstractFactory ищет сервис по имени типа. Если класс Blog\Service\ReadingTime не зарегистрирован в service_manager, вы получите исключение ServiceNotFoundException с именем этого класса — хотя ошибка возникнет при создании контроллера. Читайте в сообщении имя сервиса и регистрируйте именно его.