Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

RFC: Extract and Extend Request Mapping Logic Beyond Controllers #60312

Copy link
Copy link
Open
@sfmok

Description

@sfmok
Issue body actions

Description

Motivation

Symfony provides attributes like #[MapRequestPayload], #[MapQueryParameter], and #[MapUploadedFile] to conveniently map request data to typed objects (DTOs) in controller actions. These attributes are resolved using Symfony’s argument value resolvers and provide a powerful declarative experience — including automatic validation, format handling, and integration with the Serializer and Validator components.

However, this functionality is currently limited to controller arguments. In many real-world applications, developers frequently need to map and validate request data outside of controllers — for example:

  • In custom authenticators
  • In event listeners or middleware
  • In reusable services handling Request objects

Currently, developers must reimplement mapping and validation logic manually in these contexts, leading to duplicated effort, inconsistent behavior, and poor DX (developer experience).

Goal

Enable the same mapping and validation behavior available via #[MapRequestPayload], #[MapQueryParameter], and #[MapUploadedFile] in any Symfony service, not just controller actions.

Proposal

  1. Extract the core logic of the existing attribute-based mapping system into reusable internal services:

    • PayloadMapper — handles deserialization of JSON/XML/etc. from the request body
    • QueryParameterMapper — maps query string data into DTOs
    • UploadedFileMapper — extracts and maps uploaded files

These will encapsulate the transformation logic without coupling to the controller resolver system.

  1. Introduce a high-level public API: RequestDataMapper
    This service:

    • Combines mapping and validation
    • Uses the mappers above
    • Reproduces the exact behavior of controller argument resolution
    • Throws ValidationFailedException on constraint violations
    • Designed for use in custom authenticators, listeners, services, etc.

Example usage:

$dto = $this->requestDataMapper->mapAndValidatePayload($request, LoginRequest::class);

Design
Low-level mappers (internal services)

  • PayloadMapper::map(Request $request, string $class): object
  • QueryParameterMapper::map(Request $request, string $class): object
  • UploadedFileMapper::map(Request $request, string $field): ?UploadedFile

These services handle extraction and transformation only. No validation logic.

High-level public service

final class RequestDataMapper
{
    public function __construct(
        private PayloadMapper $payloadMapper,
        private QueryParameterMapper $queryMapper,
        private UploadedFileMapper $fileMapper,
        private ValidatorInterface $validator
    ) {}

    public function mapAndValidatePayload(Request $request, string $class, array $serializerContext = [], array|string|null $validationGroups = null, ?string $format = null): object;

    public function mapAndValidateQuery(Request $request, string $class): object;

    public function mapUploadedFile(Request $request, string $field): ?UploadedFile;
}

Integration

  • No changes to controller attribute behavior. The #[MapRequestPayload] and other attributes will continue to use the current value resolver mechanism.
  • Internally, these resolvers will be refactored to delegate to the new mappers, avoiding duplicated logic and ensuring consistency.
  • Developers can inject RequestDataMapper into any service via autowiring.

Backward Compatibility

  • This change is fully backward-compatible.
  • All existing controller attribute behavior remains unchanged.
  • The new mappers and RequestDataMapper are additive and opt-in.

Implementation Plan
If accepted, I propose to:

  1. Extract existing logic from the controller value resolvers into three private mappers:

    • PayloadMapper
    • QueryParameterMapper
    • UploadedFileMapper
  2. Introduce a new public service RequestDataMapper that:

    • Uses the above
    • Handles validation
    • Mirrors controller behavior
  3. Update the current value resolvers to delegate to the new services

  4. Add functional and unit tests for:

    • Each mapper individually
    • Combined RequestDataMapper usage
    • Exception handling and validation
  5. Update Symfony documentation (if needed) with examples for RequestDataMapper.

Conclusion
This RFC proposes a clean, fully backward-compatible enhancement to Symfony's request handling model. It extracts and elevates the powerful attribute-based mapping logic into reusable, injectable services — aligning with Symfony’s philosophy of modularity and DX excellence.

It closes a common gap experienced by developers working with authenticators, listeners, and service-level request data access.

Example

Use Cases

  1. Authenticators
public function authenticate(Request $request): PassportInterface
{
    $dto = $this->requestDataMapper->mapAndValidatePayload($request, LoginRequest::class);

    return new Passport(
        new UserBadge($dto->username),
        new PasswordCredentials($dto->password)
    );
}
  1. Event Subscribers
public function onKernelRequest(RequestEvent $event): void
{
    $filter = $this->requestDataMapper->mapAndValidateQuery($event->getRequest(), FilterDto::class);
    // ...
}
  1. Services
$dto = $this->requestDataMapper->mapAndValidatePayload($request, SearchCriteria::class);

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFCRFC = Request For Comments (proposals about features that you want to be discussed)RFC = Request For Comments (proposals about features that you want to be discussed)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.