Symfony 5.x独立使用Routing组件:如何为控制器注入参数?
解决Symfony 5.x组件中控制器依赖注入问题
一、构造函数注入实现(推荐,配置简单)
因为你已经接受构造函数注入的方式,这种方式无需额外扩展参数解析器,仅需通过DI容器配置服务即可实现嵌套依赖的自动实例化。
1. 配置DI容器服务
创建config/services.yaml文件,注册所有需要的服务(包括嵌套依赖):
services: # 注册AgentRepository的嵌套依赖,以PDO为例 database_connection: class: PDO arguments: - 'mysql:host=localhost;dbname=your_database' - 'your_username' - 'your_password' # 注册AgentRepository,自动注入PDO依赖 App\Repository\AgentRepository: autowire: true arguments: $pdo: '@database_connection' # 注册ShopController,自动注入AgentRepository依赖 App\Controller\ShopController: autowire: true
2. 初始化DI容器并关联HttpKernel
在启动文件中加载容器配置,并用容器版控制器解析器替换默认实现:
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; use App\Controller\ShopController; // 1. 初始化DI容器 $containerBuilder = new ContainerBuilder(); $loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__.'/config')); $loader->load('services.yaml'); $containerBuilder->compile(); // 2. 定义路由 $routes = new RouteCollection(); $routes->add( 'shop_info', new Route('/api/v1/shop/info', [ '_controller' => [ShopController::class, 'index'], ]) ); // 3. 创建路由匹配器 $context = new RequestContext(); $context->fromRequest(Request::createFromGlobals()); $matcher = new UrlMatcher($routes, $context); // 4. 使用容器版控制器解析器(自动从容器获取控制器实例) $controllerResolver = new ContainerControllerResolver($containerBuilder); $argumentResolver = new ArgumentResolver(); // 5. 构建HttpKernel并处理请求 $kernel = new HttpKernel($matcher, $controllerResolver, null, $argumentResolver); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send();
此时你的构造函数注入版本的ShopController就能正常工作,容器会自动实例化AgentRepository及其所有嵌套依赖。
二、方法参数注入实现(自定义ArgumentResolver)
如果偏好方法参数注入,需要实现自定义参数解析器,让它从DI容器中获取类型提示的依赖。
1. 创建自定义参数解析器
实现ArgumentValueResolverInterface,用于识别并注入容器中的服务:
namespace App\Resolver; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; class ContainerArgumentResolver implements ArgumentValueResolverInterface { private ContainerInterface $container; public function __construct(ContainerInterface $container) { $this->container = $container; } public function supports(Request $request, ArgumentMetadata $argument): bool { // 判断参数类型是否在DI容器中已注册为服务 return $argument->getType() && $this->container->has($argument->getType()); } public function resolve(Request $request, ArgumentMetadata $argument): iterable { // 从容器获取对应服务实例并返回 yield $this->container->get($argument->getType()); } }
2. 在DI容器中注册自定义解析器
修改config/services.yaml,添加自定义解析器的服务配置:
services: # ... 保留之前的服务配置 ... App\Resolver\ContainerArgumentResolver: arguments: $container: '@service_container'
3. 将自定义解析器加入参数解析链
在启动文件中,把自定义解析器添加到ArgumentResolver的解析器列表中:
// ... 保留之前的容器初始化、路由定义代码 ... // 获取自定义解析器实例 $containerResolver = $containerBuilder->get(App\Resolver\ContainerArgumentResolver::class); // 构建包含自定义解析器的参数解析器链 $argumentResolver = new ArgumentResolver([ new \Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver(), new \Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver(), $containerResolver, // 加入自定义解析器 ]); // 构建HttpKernel并处理请求 $kernel = new HttpKernel($matcher, $controllerResolver, null, $argumentResolver); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send();
此时你的方法参数注入版本的ShopController就能正常工作,自定义解析器会自动将AgentRepository注入到index方法的参数中。
内容的提问来源于stack exchange,提问作者Andrei
相关产品推荐
相关产品推荐

