symfony7.2控制器参数自动reslove的使用:
https://symfony.com/doc/current/controller/value_resolver.html#controller-argument-value-resolver
其中有一句描述很重要:
This tag is automatically added to every service implementing ValueResolverInterface, but you can set it yourself to change its priority or name attributes.
To ensure your resolvers are added in the right position you can run the following command to see which argument resolvers are present and in which order they run:
root@ca4989b3147e:/app# php bin/console debug:container debug.argument_resolver.inner --show-arguments
Information for Service "debug.argument_resolver.inner"
=======================================================
Responsible for resolving the arguments passed to an action.
---------------- -----------------------------------------------------------------------------------------------------
Option Value
---------------- -----------------------------------------------------------------------------------------------------
Service ID debug.argument_resolver.inner
Class Symfony\Component\HttpKernel\Controller\ArgumentResolver
Tags -
Public no
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired no
Autoconfigured no
Arguments Service(argument_metadata_factory)
Iterator (25 element(s))
- Service(.debug.value_resolver.security.user_value_resolver)
- Service(.debug.value_resolver.security.security_token_value_resolver)
- Service(.debug.value_resolver.doctrine.orm.entity_value_resolver)
- Service(.debug.value_resolver.argument_resolver.backed_enum_resolver)
- Service(.debug.value_resolver.argument_resolver.datetime)
- Service(.debug.value_resolver.argument_resolver.request_attribute)
- Service(.debug.value_resolver.argument_resolver.request)
- Service(.debug.value_resolver.argument_resolver.session)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\AddonCreateUpdateDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\CustomFieldCreateUpdateDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\DiscountCreateUpdateDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\EventContributionTypeDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\EventPublicationOptionDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\EventReviewOverallRecommendationDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\EventSubmissionMarkingSchemeDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\ProcessPaymentDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\ProposalCreateUpdateDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\RegistrationInformationDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\SectionCreateUpdateDtoResolver)
- Service(.debug.value_resolver.App\Model\Dto\Resolver\TicketCreateUpdateDtoResolver)
- Service(.debug.value_resolver.App\ValueResolver\FileUploadResolver)
- Service(.debug.value_resolver.argument_resolver.service)
- Service(.debug.value_resolver.argument_resolver.default)
- Service(.debug.value_resolver.argument_resolver.variadic)
- Service(.debug.value_resolver.argument_resolver.not_tagged_controller)
Service(.service_locator.ubj5Sie)
Usages debug.argument_resolver
---------------- -----------------------------------------------------------------------------------------------------
! [NOTE] The "debug.argument_resolver.inner" service or alias has been removed or inlined when the container was
! compiled.
原来使用的是:
#[MapRequestPayload] 来实现参数自动映射功能,
和
#[MapEntity(id: 'eventId')] 实现Entity映射功能
上面这两个都extends ValueResolver
不同的是,现在使用的是ValueResolverInterface, 这个是 use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
Symfony的Resolver注入原理主要基于依赖注入和参数解析机制:
类型自动识别:
根据控制器方法的参数类型
自动匹配对应的ValueResolver
通过类型注入和推断实现参数解析
我自己测试发现,只要这样定义
class EventSessionCreateEditDtoResolver extends AbstractRequestDtoResolver // 最终implemanets ValueResolverInterface
那么 EventSessionCreateEditDto 都可以被自动注入
//查看指定resolver的注入情况:
root@ca4989b3147e:/app# php bin/console debug:container App\Model\Dto\Resolver\EventSessionCreateEditDtoResolver
Information for Service "App\Model\Dto\Resolver\EventSessionCreateEditDtoResolver"
==================================================================================
---------------- --------------------------------------------------------------------------------
Option Value
---------------- --------------------------------------------------------------------------------
Service ID App\Model\Dto\Resolver\EventSessionCreateEditDtoResolver
Class App\Model\Dto\Resolver\EventSessionCreateEditDtoResolver
Tags controller.argument_value_resolver
Public no
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired yes
Autoconfigured yes
Usages .debug.value_resolver.App\Model\Dto\Resolver\EventSessionCreateEditDtoResolver
---------------- --------------------------------------------------------------------------------
! [NOTE] The "App\Model\Dto\Resolver\EventSessionCreateEditDtoResolver" service or alias has been removed or inlined
! when the container was compiled.
Symfony会自动注入。核心机制是:
通过EventSessionCreateEditDtoResolver专门处理EventSessionCreateEditDto类型
控制器方法参数声明了Dto\Input\EventSessionCreateEditDto类型
ArgumentResolver会调用匹配的Resolver
Resolver负责创建或解析该对象实例
解析流程:
匹配参数类型
调用对应Resolver
创建EventSessionCreateEditDto实例
注入到控制器方法
其实最主要的一个方法是:
public function resolve(Request $request, ArgumentMetadata $argument): iterable
{
if ($argument->getType() !== $this->getSupportedType()) {
return [];
}
$deserializedDto = $this->serializer->deserialize($request->getContent(), $this->getSupportedType(), 'json');
$dtoObject = $this->postResolve($deserializedDto);
$violations = $this->validator->validate($dtoObject);
if (\count($violations)) {
throw new HttpException(Response::HTTP_UNPROCESSABLE_ENTITY, implode("\n", array_map(static fn ($e) => $e->getMessage(), iterator_to_array($violations))), new ValidationFailedException($request->getPayload(), $violations));
}
return [$dtoObject];
}
其中:
if ($argument->getType() !== $this->getSupportedType()) {
return [];
}
这个$this->getSupportedType() 决定了,当前这个resolver会对哪个控制器的参数生效,如果没有这个判断,就会对所有的参数生效,这也是symfony的一个特色。和 EventSessionCreateEditDtoResolver 这个类名没关系,可以任意命名,最后都可以被请求到。我一直是根据这个类命名来决定处理哪个Dto的,其实不是的,就是根据上面的判断来决定对哪个类型($argument->getType())生效。
再来看文档中的这句:
This tag is automatically added to every service implementing ValueResolverInterface. 说明只要定义了resolver, 实现了ValueResolverInterface,都会被注入,最后对哪个Dto生效,取决于前面的if 类型判断。
有人出现郭这个问题:
https://stackoverflow.com/questions/79060922/symfony-7-x-custom-resolver-always-called
同时还可以在service.yaml文件下配置(而不影响使用和之前一样的效果):
App\Model\Dto\Resolver\AddonCreateUpdateDtoResolver:
tags:
- controller.argument_value_resolver:
name: addon_dto //取个名字
priority: 150 //设置优先级
// 再次查看
root@ca4989b3147e:/app# php bin/console debug:container App\Model\Dto\Resolver\AddonCreateUpdateDtoResolver
Information for Service "App\Model\Dto\Resolver\AddonCreateUpdateDtoResolver"
=============================================================================
---------------- ---------------------------------------------------------------------------
Option Value
---------------- ---------------------------------------------------------------------------
Service ID App\Model\Dto\Resolver\AddonCreateUpdateDtoResolver
Class App\Model\Dto\Resolver\AddonCreateUpdateDtoResolver
Tags controller.argument_value_resolver (name: addon_dto, priority: 150)
controller.argument_value_resolver
Public no
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired yes
Autoconfigured yes
Usages .debug.value_resolver.App\Model\Dto\Resolver\AddonCreateUpdateDtoResolver
---------------- ---------------------------------------------------------------------------
! [NOTE] The "App\Model\Dto\Resolver\AddonCreateUpdateDtoResolver" service or alias has been removed or inlined when
! the container was compiled.
证明了文档的这句:
but you can set it yourself to change its priority or name attributes.
总结:
这个功能和larvel下自定义request验证类似,都可以提前获取参数,以及格式化参数(添加或者删除部分参数).
