Як визначати контролери як сервіси

Дата оновлення перекладу 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.