Как определять контроллеры как сервисы
Дата обновления перевода 2025-08-23
Как определять контроллеры как сервисы
В Symfony контроллер не должен быть зарегистрирован как сервис. Но если вы используете конфигурацию services.yaml по умолчанию , и ваши контроллеры расширяют класс AbstractController, они уже автоматически зарегистрированы, как сервисы. Это означает, что вы можете использовать внедрение зависимостей, как в любой другой нормальный сервис.
Если вы не хотите расширять класс AbstractController
, вы можете зарегистрировать ваши
контроллери как сервисы несколькими способами:
- Используя атрибут
#[Route]
; - Используя атрибут
#[AsController]
; - Используя тег сервиса
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.