跳到内容

如何将控制器定义为服务

编辑此页

在 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 许可获得许可。
目录
    版本