Как определять контроллеры как сервисы

Дата обновления перевода 2025-08-23

Как определять контроллеры как сервисы

В Symfony контроллер не должен быть зарегистрирован как сервис. Но если вы используете конфигурацию services.yaml по умолчанию , и ваши контроллеры расширяют класс AbstractController, они уже автоматически зарегистрированы, как сервисы. Это означает, что вы можете использовать внедрение зависимостей, как в любой другой нормальный сервис.

Если вы не хотите расширять класс AbstractController, вы можете зарегистрировать ваши контроллери как сервисы несколькими способами:

  1. Используя атрибут #[Route];
  2. Используя атрибут #[AsController];
  3. Используя тег сервиса controller.service_arguments.

Используя атрибут #[Route]

При использовании атрибута #[Route] для определения
маршрутов в любом классе PHP, Symfony рассматривает этот класс как контроллер. Она регистрирует его как публичный неленивый серви и включает внедрение аргументов сервиса во всех его методах.

Это самый простой и рекомендованный способ регистрации контроллеров как сервисов, когда не расширяется базовый класс контроллера.

7.3

Функция регистрации контроллеров как сервисов, используя атрибут #[Route], была представлена в Symfony 7.3.

Используя атрибут #[AsController]

Если вам так больше нравится, вы можете использовать PHP-атрибут #[AsController], чтобы автоматически применять тег controller.service_arguments к вашим сервисам контроллера:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/Controller/HelloController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Attribute\Route;

#[AsController]
class HelloController
{
    #[Route('/hello', name: 'hello', methods: ['GET'])]
    public function index(): Response
    {
        // ...
    }
}

Tip

При использовании атрибута #[Route], Symfony уже регистрирует класс контроллера как сервис, поэтому использование атрибута #[AsController] излишне.

Используя тег сервиса controller.service_arguments

Если ваши контроллеры не расширяют класс AbstractController и вы не используете атрибуты #[AsController] или #[Route], вы должны зарегистрировать контроллеры как публичные сервисы вручную и применить тег сервиса controller.service_arguments, чтобы включить внедрение сервиса в действиях контроллера:

1
2
3
4
5
6
7
# config/services.yaml

# контроллеры импортируются отдельно, чтобы гарантировать, что сервисы могут быть внедрены как
# аргументы действия, даже если вы не расширяете ни один базовый класс контроллера
App\Controller\:
   resource: '../src/Controller/'
   tags: ['controller.service_arguments']

Note

Если вы не используете автомонтирование или автоконфигурацию и вы расширяете AbstractController, вам будет необходимо применить другие теги и сделать некоторые вызовы методом, чтобы зарегистрировать ваши контроллеры как сервисы:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# config/services.yaml

# эта расширенная конфигурация необходима только в случае, если не используется автомонтирование/автоконфигурация,
# что мало распространено и не рекомендуется

abstract_controller.locator:
    class: Symfony\Component\DependencyInjection\ServiceLocator
    arguments:
        -
            router: '@router'
            request_stack: '@request_stack'
            http_kernel: '@http_kernel'
            session: '@session'
            parameter_bag: '@parameter_bag'
            # вы можете добавить больше сервисов здесь, когда они вам понадобятся (например, сервис `serializer`)
            # и посмотреть на класс AbstractController, чтобы увидеть, какие сервисы определены в локаторе

App\Controller\:
    resource: '../src/Controller/'
    tags: ['controller.service_arguments']
    calls:
        - [setContainer, ['@abstract_controller.locator']]

Регистрация вашего контроллера как сервися является первым шагом, но вам также нужно обновить конфигурацию маршрутизации, чтобы правильно ссылаться на сервис, чтобы Symfony знала, как его использовать.

Используйте синтаксис service_id::method_name для ссылания на метод контроллера. Если идентификатор сервиса является полностью квалифицированным именем класса (FQCN) вашего контроллера, как рекомендует Symfony, то синтаксис будет таким же, как и если контроллер не был бы сервисом, например App\Controller\HelloController::index:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Controller/HelloController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class HelloController
{
    #[Route('/hello', name: 'hello', methods: ['GET'])]
    public function index(): Response
    {
        // ...
    }
}

Вызываемые контроллеры

Контроллеры также могут определять одно действие, используя метод __invoke(), что является распространенной практикой при следовании паттерну ADR (Действие-Домен-Ответчик):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Controller/Hello.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/hello/{name}', name: 'hello')]
class Hello
{
    public function __invoke(string $name = 'World'): Response
    {
        return new Response(sprintf('Hello %s!', $name));
    }
}

Альтернативы базовых методов контроллера

При использовании контроллера, определённого как сервиса, вы всё ещё можете расширить базовый контроллер AbstractController , и использовать его сокращения. Но, вы не обязаны это делать! Вы можете выбрать не расширять ничего и использовать внедрение зависимоти, чтобы получить доступ к разным сервисам.

Базовый исходный код класса контроллера - это отличный способ увидеть, как можно добиться выполнения общих задач. Например, $this->render() обычно используется для отображения шаблона Twig и возвращения Ответа. Но, вы также можете сделать это напрямую:

В контроллере, определённом как сервис, вы можете вместо этого внедрить сервис twig и использовать его напрямую:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// src/Controller/HelloController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;

class HelloController
{
    public function __construct(
        private Environment $twig,
    ) {
    }

    public function index(string $name): Response
    {
        $content = $this->twig->render(
            'hello/index.html.twig',
            ['name' => $name]
        );

        return new Response($content);
    }
}

Вы также можете использовать специальное внедрение зависимости на основе действий , чтобы получаь сервисы в качестве аргументов ваших методов действий контроллера.

Базовые методы контроллера и их сервисные замены

Наилучший способ увидеть, как заменить базовые воспомогательные методы Controller - это посмотреть на класс AbstractController, содержащий его логику.

Если вы хотите узнать, какое типизирование использовать для каждого сервиса, смотрите метод getSubscribedServices() в AbstractController.