跳至內容

如何將控制器定義為服務

編輯此頁

在 Symfony 中,控制器需要註冊為服務。但是,如果您正在使用預設的 services.yaml 配置,並且您的控制器擴展了 AbstractController 類別,它們自動註冊為服務。這表示您可以像任何其他正常服務一樣使用依賴注入。

如果您的控制器沒有擴展 AbstractController 類別,您必須明確地將您的控制器服務標記為 public。或者,您可以將 controller.service_arguments 標籤應用於您的控制器服務。這將使標記的服務成為 public,並允許您在方法參數中注入服務

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

# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
   resource: '../src/Controller/'
   tags: ['controller.service_arguments']

注意

如果您不使用自動裝配自動配置,並且您擴展了 AbstractController,您將需要應用其他標籤並進行一些方法調用以將您的控制器註冊為服務

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

# this extended configuration is only required when not using autowiring/autoconfiguration,
# which is uncommon and not recommended

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'
            # you can add more services here as you need them (e.g. the `serializer`
            # service) and have a look at the AbstractController class to see
            # which services are defined in the locator

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

如果您願意,您可以使用 #[AsController] PHP 屬性來自動將 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
    {
        // ...
    }
}

將您的控制器註冊為服務是第一步,但您還需要更新您的路由配置以正確引用該服務,以便 Symfony 知道要使用它。

使用 service_id::method_name 語法來引用控制器方法。如果服務 ID 是您的控制器的完全限定類別名稱 (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 模式 (Action-Domain-Responder) 時的常見做法

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 基礎控制器 並使用其快捷方式。但是,您不需要這樣做!您可以選擇不擴展任何東西,並使用依賴注入來存取不同的服務。

基礎 Controller 類別原始碼 是了解如何完成常見任務的好方法。例如,$this->render() 通常用於呈現 Twig 範本並回傳 Response。但是,您也可以直接執行此操作

在定義為服務的控制器中,您可以改為注入 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 類別。

如果您想知道每個服務要使用什麼型別提示,請參閱 AbstractController 中的 getSubscribedServices() 方法。

本作品,包括程式碼範例,依據 Creative Commons BY-SA 3.0 授權條款授權。
目錄
    版本