PHPackages                             macpaw/request-dto-resolver - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. macpaw/request-dto-resolver

ActiveSymfony-bundle

macpaw/request-dto-resolver
===========================

Request DTO resolver bundle

v1.1.1(2mo ago)125.0k↓29.3%MITPHPPHP &gt;=8.0CI passing

Since Feb 15Pushed 2mo ago15 watchersCompare

[ Source](https://github.com/MacPaw/request-dto-resolver)[ Packagist](https://packagist.org/packages/macpaw/request-dto-resolver)[ Docs](https://github.com/MacPaw/request-dto-resolver)[ RSS](/packages/macpaw-request-dto-resolver/feed)WikiDiscussions develop Synced 1mo ago

READMEChangelog (7)Dependencies (22)Versions (12)Used By (0)

Symfony Request DTO Resolver Bundle
===================================

[](#symfony-request-dto-resolver-bundle)

Automatically resolves and validates Symfony HTTP request data (JSON, form-data, query parameters) into DTOs.

Features
--------

[](#features)

- Automatic resolution of request data into DTOs.
- Seamless support for JSON, form-data, and query string parameters.
- Built-in validation using the Symfony Form component.
- Support for complex nested data structures.
- Customizable parameter resolution order and field mapping.
- Smart integration with other bundles that parse the request body.

Installation
------------

[](#installation)

```
composer require macpaw/request-dto-resolver
```

The bundle should be automatically registered in your `config/bundles.php`. If not, add it manually:

```
// config/bundles.php
return [
    RequestDtoResolver\RequestDtoResolverBundle::class => ['all' => true],
    // ...
];
```

Configuration
-------------

[](#configuration)

First, define an interface that your DTOs will implement. This allows the resolver to identify which arguments to process.

```
// src/DTO/RequestDtoInterface.php
namespace App\DTO;

interface RequestDtoInterface
{
}
```

Then, point the bundle to this interface in a configuration file:

```
# config/packages/request_dto_resolver.yaml
request_dto_resolver:
    target_dto_interface: App\DTO\RequestDtoInterface
```

How It Works
------------

[](#how-it-works)

The resolver uses a combination of a DTO class and a Symfony Form to process and validate incoming request data.

1. **Controller Argument**: You type-hint a controller argument with your DTO class (e.g., `UserDto`).
2. **FormType Attribute**: You decorate the controller action with the `#[FormType]` attribute, specifying which Symfony Form to use for processing.
3. **Data Resolution**: The resolver extracts data from the request based on the form's fields.
4. **Validation**: The form validates the data against the constraints defined in your DTO.
5. **DTO Hydration**: If validation passes, a new DTO instance is created and populated with the validated data.

Usage
-----

[](#usage)

### 1. Create a DTO

[](#1-create-a-dto)

The DTO is a simple PHP class that implements your marker interface. Use Symfony's Validator components to define constraints.

```
// src/DTO/UserDto.php
namespace App\DTO;

use Symfony\Component\Validator\Constraints as Assert;

class UserDto implements RequestDtoInterface
{
    #[Assert\NotBlank]
    #[Assert\Length(min: 3)]
    public string $name;

    #[Assert\NotBlank]
    #[Assert\Email]
    public string $email;

    /** @var string[] */
    #[Assert\Count(min: 1)]
    #[Assert\All([
        new Assert\NotBlank,
        new Assert\Length(min: 2)
    ])]
    public array $tags = [];
}
```

### 2. Create a Form Type

[](#2-create-a-form-type)

The Form Type defines the structure of the expected request data and maps it to your DTO.

```
// src/Form/UserFormType.php
namespace App\Form;

use App\DTO\UserDto;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class)
            ->add('email', EmailType::class)
            ->add('tags', CollectionType::class, [
                'entry_type' => TextType::class,
                'allow_add' => true,
            ]);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => UserDto::class,
        ]);
    }
}
```

### 3. Use in a Controller

[](#3-use-in-a-controller)

In your controller, type-hint the action argument with your DTO class and add the `#[FormType]` attribute.

```
// src/Controller/UserController.php
namespace App\Controller;

use App\DTO\UserDto;
use App\Form\UserFormType;
use RequestDtoResolver\Attribute\FormType;
use Symfony\Component\HttpFoundation\JsonResponse;

class UserController
{
    #[FormType(UserFormType::class)]
    public function __invoke(UserDto $dto): JsonResponse
    {
        // $dto is now a validated and populated object
        return new JsonResponse([
            'name' => $dto->name,
            'email' => $dto->email,
            'tags' => $dto->tags,
        ]);
    }
}
```

Parameter Resolution
--------------------

[](#parameter-resolution)

The resolver automatically extracts data from the request to populate the form. The source of the data depends on the request's `Content-Type` header and method.

### Resolution Order

[](#resolution-order)

For each field defined in your Form Type, the resolver searches for a corresponding value in the following order:

1. **JSON Body**: If the request has a `Content-Type` of `application/json`, the decoded JSON body is checked first.
2. **Query &amp; Form Data**: The resolver then checks `request->query` (for `GET` parameters) and `request->request` (for `POST` form data).
3. **Request Headers**: Finally, it checks the request headers.

This order means that for a `POST` request with both a JSON body and query parameters, the values in the **JSON body will take precedence**.

### Common Scenarios

[](#common-scenarios)

- **POST with JSON Body**: `{"name": "John"}` -&gt; `name` is resolved from JSON.
- **POST with Form Data**: `name=John` -&gt; `name` is resolved from form data.
- **GET with Query Parameters**: `?name=John` -&gt; `name` is resolved from query string.
- **GET with `Content-Type: application/json`**: The resolver will correctly ignore the header and still pull data from the query string, preventing malformed body errors.
- **Request without `Content-Type`**: The request is treated as a standard form/query request, and data is resolved from the query string.

Advanced Features
-----------------

[](#advanced-features)

### Custom Field Mapping

[](#custom-field-mapping)

You can map request fields to different DTO properties using the `lookupKey` option in your Form Type. This is useful for handling request keys that don't match your DTO property names (e.g., `user-id` vs. `userId`).

**Form Type Configuration:**

```
// ...
$builder->add('userId', TextType::class, [
    'attr' => ['lookupKey' => 'user-id'],
]);
// ...
```

This configuration will map the `user-id` key from any source (JSON body, query, or header) to the `userId` form field.

**Request Example:**

```
POST /api/some-endpoint
Content-Type: application/json

{
    "user-id": 123
}
```

Integration with Other Bundles
------------------------------

[](#integration-with-other-bundles)

This bundle is designed to work seamlessly with other bundles that parse the request body (e.g., FOSRestBundle). If the request body is already parsed and populated in `$request->request`, the resolver will automatically use this pre-parsed data instead of reading the raw body again.

This ensures:

- No double-parsing of the request body.
- Consistent validation and mapping rules.
- Zero-configuration interoperability.

Error Handling
--------------

[](#error-handling)

The bundle throws the following exceptions, which you can handle with a standard Symfony exception listener:

- `InvalidParamsDtoException`: For validation errors (contains a `ConstraintViolationList`).
- `BadRequestHttpException`: For a malformed JSON body.
- `UnsupportedMediaTypeHttpException`: For an unsupported `Content-Type`.
- `MissingFormTypeAttributeException`: When the `#[FormType]` attribute is missing on the controller action.

Contributing
------------

[](#contributing)

Feel free to open issues and submit pull requests.

License
-------

[](#license)

This bundle is released under the MIT license.

###  Health Score

49

—

FairBetter than 95% of packages

Maintenance88

Actively maintained with recent releases

Popularity29

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor3

3 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~109 days

Recently: every ~89 days

Total

8

Last Release

60d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1935412?v=4)[MacPaw Inc.](/maintainers/macpaw)[@MacPaw](https://github.com/MacPaw)

---

Top Contributors

[![ganzyukv](https://avatars.githubusercontent.com/u/12824765?v=4)](https://github.com/ganzyukv "ganzyukv (10 commits)")[![Yozhef](https://avatars.githubusercontent.com/u/8971757?v=4)](https://github.com/Yozhef "Yozhef (10 commits)")[![cursoragent](https://avatars.githubusercontent.com/u/199161495?v=4)](https://github.com/cursoragent "cursoragent (8 commits)")[![serhiidonii](https://avatars.githubusercontent.com/u/158484424?v=4)](https://github.com/serhiidonii "serhiidonii (6 commits)")[![zakhar-huzenko](https://avatars.githubusercontent.com/u/26282904?v=4)](https://github.com/zakhar-huzenko "zakhar-huzenko (4 commits)")[![cod-a-holic](https://avatars.githubusercontent.com/u/15779118?v=4)](https://github.com/cod-a-holic "cod-a-holic (3 commits)")[![ci-macpaw](https://avatars.githubusercontent.com/u/31448939?v=4)](https://github.com/ci-macpaw "ci-macpaw (1 commits)")

---

Tags

backendmacpawsymfonyrequestresolverdto

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Rector

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/macpaw-request-dto-resolver/health.svg)

```
[![Health](https://phpackages.com/badges/macpaw-request-dto-resolver/health.svg)](https://phpackages.com/packages/macpaw-request-dto-resolver)
```

###  Alternatives

[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M651](/packages/sylius-sylius)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.0k15.4k](/packages/prestashop-prestashop)[kimai/kimai

Kimai - Time Tracking

4.6k7.4k1](/packages/kimai-kimai)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[ec-cube/ec-cube

EC-CUBE EC open platform.

78527.0k1](/packages/ec-cube-ec-cube)[netgen/layouts-core

Netgen Layouts enables you to build and manage complex web pages in a simpler way and with less coding. This is the core of Netgen Layouts, its heart and soul.

3689.4k10](/packages/netgen-layouts-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
