В современном веб‑разработке важна архитектура, позволяющая быстро менять поведение приложения. Yii2 предоставляет мощный контейнер зависимостей, который позволяет внедрять сервисы в контроллеры без глобальных синглтонов. В этой статье мы разберём, как зарегистрировать сервисы в контейнере и использовать их в контроллерах, используя примеры Yii2 dependency injection. Такой подход упрощает тестирование и повышает гибкость кода.

Разбор задачи

Когда приложение растёт, контроллеры начинают зависеть от множества сервисов: почтового, кеша, платежного и т.д. Если каждый контроллер сам создаёт эти сервисы или использует глобальный объект, код становится тяжеловесным и трудно тестируемым. Нужно вынести создание сервисов в один слой и позволить контейнеру управлять их жизненным циклом. В Yii2 это достигается через сервис‑локатор и контейнер зависимостей, где сервисы регистрируются как фабрики или singleton‑объекты.

Пошаговое решение

Первый шаг – определить интерфейс, который будет использовать контроллер. Далее создаём реализацию и регистрируем её в компоненте components приложения. При этом можно указать фабрику, чтобы сервис создавался по запросу. Затем в контроллере объявляем зависимость через конструктор или свойство, и Yii2 автоматически подставит нужный экземпляр, когда контроллер будет создан. Такой механизм позволяет полностью избавиться от глобальных синглтонов, улучшить тестируемость и упростить поддержку кода.

Рабочий код с комментариями

// app/config/di.php
return [
    'components' => [
        'mailer' => [
            'class' => yiiswiftmailerMailer::class,
            'transport' => [
                'class' => yiiswiftmailertransportSendmailTransport::class,
            ],
        ],
    ],
];
// Controller using constructor injection
namespace appcontrollers;
use yiiwebController;
use yiiswiftmailerMailer;

class SiteController extends Controller
{
    private $mailer;

    public function __construct($id, $module, Mailer $mailer, $config = [])
    {
        parent::__construct($id, $module, $config);
        $this->mailer = $mailer; // dependency injected
    }

    public function actionSend()
    {
        $this->mailer->compose()
            ->setTo('user@example.com')
            ->setSubject('Test')
            ->send();
    }
}

Типичные ошибки

1. Подавление ошибок при неверном типе. Если конструктор ожидает Mailer, а передаётся строка, PHP бросит TypeError. Убедитесь, что в конфиге указан правильный класс.

2. Избыточный Service Locator. Часто разработчики берут компонент из Yii::$app->mailer вместо DI, что усложняет тестирование. Предпочитайте конструктор.

3. Игнорирование конфигурационных параметров. Если компонент требует настройки (например, DSN), забыть задать это в config, и сервис будет неинициализирован.

FAQ

Как протестировать контроллер с DI?

Создайте мок Mailer и передайте его в конструктор. В тесте опустите конфиг, используйте $controller = new SiteController(‘site’, $module, $mockMailer); После этого проверяйте, что метод send был вызван.

Можно ли менять тип сервиса в рантайме?

Да, но только через контейнер. Используйте Yii::$container->set(Mailer::class, $customMailer); Это заменит глобальный биндинг для всех последующих вызовов.

Что делать, если нужно несколько реализаций одного интерфейса?

Определите разные алиасы: Yii::$container->set(‘mailService’, EmailMailer::class); и в контроллере тип интерфейса, а не конкретный класс. Тесты могут переключать реализацию, задавая другой класс под тот же алиас.

Заключение и чек‑лист

Ниже список действий, которые гарантируют правильную работу DI в Yii2:

  • Определите все сервисы в config/di.php.
  • Используйте конструктор‑инъекцию в контроллерах.
  • Не прибегайте к Service Locator, если можно.
  • Проверьте типы в конструкторе, чтобы избежать Runtime Error.
  • Тестируйте контроллеры с моками.
  • При необходимости меняйте реализацию через Yii::$container->set().

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *