Drupal → Внедрение зависимостей в контроллер

04.09.2025

В Drupal 11 три способа внедрения зависимостей в контроллер:

1. Классический, с помощью метода create()

class ExampleController implements ContainerInjectionInterface {

  public function __construct(
    protected EntityTypeManagerInterface $entityTypeManager,
    protected MessengerInterface $messenger,
  ) {}

  public static function create(ContainerInterface $container): static {
    return new static(
      $container->get(EntityTypeManagerInterface::class),
      $container->get(MessengerInterface::class),
    );
  }
  
}

Минусы — необходимость реализации метода ContainerInjectionInterface::create().
Плюсы — работает в Drupal 8+.

2. Новый, с помощью AutowireTrait

use Drupal\Core\DependencyInjection\AutowireTrait;

class ExampleController implements ContainerInjectionInterface {

  use AutowireTrait;

  public function __construct(
    #[Autowire(service: EntityTypeManagerInterface::class)]
    protected EntityTypeManagerInterface $entityTypeManager,
    #[Autowire(service: MessengerInterface::class)]
    protected MessengerInterface $messenger,
  ) {}
  
}

Если имя сервиса совпадает с именем класса или у сервиса есть соответствующий синоним, то #[Autowire(...)] можно опустить:

use Drupal\Core\DependencyInjection\AutowireTrait;

class ExampleController implements ContainerInjectionInterface {

  use AutowireTrait;

  public function __construct(
    protected EntityTypeManagerInterface $entityTypeManager,
    protected MessengerInterface $messenger,
  ) {}
  
}

Минусы — рефлексия в рантайме, урезанный autowire (например нельзя сделать так #[Autowire(param: 'app.root')).

3. Symfony-way, с помощью регистрации контроллера в качестве сервиса

// src/Controller/ExampleController.php

class ExampleController {

  public function __construct(
    protected EntityTypeManagerInterface $entityTypeManager,
    protected MessengerInterface $messenger,
  ) {}
  
}
# MODULENAME.services.yml
services:
  _defaults:
    autowire: true
  # Without leading slash
  Drupal\MODULENAME\Controller\ExampleController: ~

Плюсы — полноценный autowire.

Важное замечание — в MODULENAME.routing.yml, как и в MODULENAME.services.yml, путь к контроллеру не должен содержать начальный слэш:

MODULENAME.my_page:
  path: '/my-page'
  defaults:
    _title: 'My page'
    # Without leading slash
    _controller: Drupal\MODULENAME\Controller\ExampleController
  requirements:
    _access: 'TRUE'

Подробнее про autowire:
AutowireTrait allows ContainerInjectionInterface classes to be autowired
Services can be autowired
Defining Services Dependencies Automatically (Autowiring)

Написанное актуально для
Drupal 11+
Похожие записи

Комментарии

Гость
06.09.2025, 18:42

> Минусы — рефлексия в рантайме, урезанный autowire (например нельзя сделать так #[Autowire(param: 'app.root')).

В чистом виде Symfony сохраняет собранный контейнер в кеш и никакой рефлексии в рантайме нету. Аттрибут #[Autowire] судя по названию, это атрибут симфони, а значит никакой рефлексии в рантайме быть не должно, или в Drupal это работает иначе?

Контейнер к контроллерам никакого отношения не имеет. См \Drupal\Core\DependencyInjection\AutowireTrait::create()

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