Почему не следует напрямую обращаться к глобальному контейнеру в Joomla 5
Что общего у нижеперечисленных методов?
Joomla\CMS\Factory::getCache()
Joomla\CMS\Factory::getDbo()
Joomla\CMS\Factory::getMailer()
Joomla\CMS\Application\AdministratorApplication::getRouter()
Joomla\CMS\Application\Administrator\ApiApplication::getRouter()
Joomla\CMS\Toolbar\Toolbar::getInstance()
Они объявлены устаревшими с Joomla 4.x и вместо них предлагается использовать Joomla\CMS\Factory::getContainer()->get('Соответствующий_интерфейс')
.
Например, вместо Joomla\CMS\Factory::getDbo()
— Factory::getContainer()->get(DatabaseInterface::class)
;
Но давайте посмотрим на описание метода Joomla\CMS\Factory::getContainer()
:
Возвращает глобальный контейнер сервисов, создавая его только в случае отсутствия.
Этот метод рекомендуется использовать только в коде, ответственном за создание новых сервисов и требующем разрешения зависимостей. Его следует применять только когда контейнер недоступен другими способами.
Допустимые сценарии использования:
Статический метод
getInstance()
, вызывающий сервис из контейнера (пример:Joomla\CMS\Toolbar\Toolbar::getInstance()
);Фронт-контроллер приложения, загружающий и исполняющий класс Joomla (пример: файл
cli/joomla.php
);Получение опциональных зависимостей конструктора во время переходного периода для сохранения обратной совместимости (в этом случае следует добавлять уведомление об устаревании).
Не рекомендуется использовать этот метод как прямую замену статическим вызовам, например заменять
Factory::getDbo()
наFactory::getContainer()->get(DatabaseInterface::class)
. Вместо этого код следует рефакторить для использования внедрения зависимостей.
В последнем абзаце видим явное противоречие и рекомендацию использовать зависимости.
Как же внедрять зависимости?
Рассмотрим как это сделано в плагине joomla
группы content
:
В сервис-провайдере плагина Root/plugins/content/joomla/services/provider.php
используются методы setDatabase()
и setUserFactory()
для установки зависимостей.
$plugin->setDatabase($container->get(DatabaseInterface::class));
$plugin->setUserFactory($container->get(UserFactoryInterface::class));
А в классе расширения плагина plugins/content/joomla/src/Extension/Joomla.php
используются методы getDatabase()
и getUserFactory()
. Аналогично в компонентах.
Мой пример использования:
В компоненте обновления цен для JoomShopping, в моделях работающих с товарами я заменил драйвер базы данных на свой, соединяющийся с базой другого сайта. Код самих моделей при этом изменять не потребовалось.
Выводы:
Сервис-провайдер расширения — единая точка установки зависимостей для своего расширения.
Событие
onAfterExtensionBoot
- точка для замены зависимости в любом расширении.Если бы расширения напрямую брали зависимости из глобального контейнера, такая замена была бы невозможна.
Материалы по теме: