We have some DTO which implements DtoResolverInterface
:
interface DtoResolverInterface
{
/**
* Inject custom resolver
*
* @param OptionsResolver $resolver
*/
public function injectResolver(OptionsResolver $resolver);
/**
* Resolves received data into EntryDTO
*
* @param array $data
*/
public function resolve(array $data);
}
Register listener for onKernelControllerArguments
:
/**
* @param FilterControllerArgumentsEvent $event
*/
public function onKernelControllerArguments(FilterControllerArgumentsEvent $event): void
{
$request = $event->getRequest();
foreach ($event->getArguments() as $argument) {
if (!$argument instanceof DtoResolverInterface) {
continue;
}
$resolver = $this->factory->createForDefinition(get_class($argument));
$argument->injectResolver($resolver);
try {
$argument->resolve($request->body->all());
} catch (InvalidOptionsException $e) {
throw new ApiException(ApiException::HTTP_BAD_REQUEST_DATA, $e->getMessage());
}
}
}
So finally, in the controller method we receives already resolved DTO instead full Request:
public function updateServicePhoneNumberAction(UpdateServicePhoneNumberEntryDto $entryDto): Response
{
// some code here
}
DTO example for better understanding:
/**
* @SWG\Definition(
* type="object",
* description="DTO for updating service phone number",
* required={"servicePhoneNumber"},
* )
*/
class UpdateServicePhoneNumberEntryDto extends AbstractDtoResolver // AbstractDtoResolver implements common logic and implements DtoResolverInterface
{
/**
* @var string
*
* @SWG\Property(description="Phone number in E.164 format", example="71112223344")
*/
protected $servicePhoneNumber;
/**
* @return string
*/
public function getServicePhoneNumber(): string
{
return $this->servicePhoneNumber;
}
}