PHPackages                             crtl/request-dto-resolver-bundle - 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. [Validation &amp; Sanitization](/categories/validation)
4. /
5. crtl/request-dto-resolver-bundle

ActiveSymfony-bundle[Validation &amp; Sanitization](/categories/validation)

crtl/request-dto-resolver-bundle
================================

Deserializes and validates requests into objects

v3.1.0(3mo ago)1164↓50%[1 issues](https://github.com/crtl/request-dto-resolver-bundle/issues)MITPHPPHP &gt;=8.2CI failing

Since Jun 3Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/crtl/request-dto-resolver-bundle)[ Packagist](https://packagist.org/packages/crtl/request-dto-resolver-bundle)[ RSS](/packages/crtl-request-dto-resolver-bundle/feed)WikiDiscussions 2.x Synced 1mo ago

READMEChangelog (10)Dependencies (15)Versions (18)Used By (0)

crtl/request-dto-resolver-bundle
================================

[](#crtlrequest-dto-resolver-bundle)

[![codecov](https://camo.githubusercontent.com/c9aa4e5b3ef2b49eec7c67606cea752491cca09d5bab370f1ab81a6fdd36ddd2/68747470733a2f2f636f6465636f762e696f2f67682f6372746c2f726571756573742d64746f2d7265736f6c7665722d62756e646c652f6272616e63682f322e782f67726170682f62616467652e7376673f746f6b656e3d564c58444a3932355438)](https://codecov.io/gh/crtl/request-dto-resolver-bundle)[![Latest Stable Version](https://camo.githubusercontent.com/244ca8b13593a5b10687141ac2e5038f5d4b9c1786f7ac2dff1d268f949f0cc8/687474703a2f2f706f7365722e707567782e6f72672f6372746c2f726571756573742d64746f2d7265736f6c7665722d62756e646c652f76)](https://packagist.org/packages/crtl/request-dto-resolver-bundle)[![Total Downloads](https://camo.githubusercontent.com/50676a562b0c6860fb19076313dc16f4210409a1c715fed9968e423566d791cf/687474703a2f2f706f7365722e707567782e6f72672f6372746c2f726571756573742d64746f2d7265736f6c7665722d62756e646c652f646f776e6c6f616473)](https://packagist.org/packages/crtl/request-dto-resolver-bundle)[![Latest Unstable Version](https://camo.githubusercontent.com/e260ca98d856b34953c45b615ce790af78cc40a97b645647ae46b56afc9b0e16/687474703a2f2f706f7365722e707567782e6f72672f6372746c2f726571756573742d64746f2d7265736f6c7665722d62756e646c652f762f756e737461626c65)](https://packagist.org/packages/crtl/request-dto-resolver-bundle)[![License](https://camo.githubusercontent.com/a42712ab0557a8b47f0b5a7cdb6e682a0bb8f25eaea0e34f98730500cdcfca4d/687474703a2f2f706f7365722e707567782e6f72672f6372746c2f726571756573742d64746f2d7265736f6c7665722d62756e646c652f6c6963656e7365)](https://packagist.org/packages/crtl/request-dto-resolver-bundle)[![PHP Version Require](https://camo.githubusercontent.com/85d90dfde37c0f49c53f349e0cf8b56a12065fe59dcd6d99d0e34c529bef3011/687474703a2f2f706f7365722e707567782e6f72672f6372746c2f726571756573742d64746f2d7265736f6c7665722d62756e646c652f726571756972652f706870)](https://packagist.org/packages/crtl/request-dto-resolver-bundle)

A Symfony bundle for predictable, type-safe instantiation and validation of request DTOs.

It removes boilerplate from controllers while staying close to Symfony’s native validation and argument resolving mechanisms.

Features
--------

[](#features)

- **Automatic DTO Resolution**
    DTOs type-hinted in controller actions are instantiated and validated automatically.
- **Native Symfony Validator Integration**
    Uses Symfony’s `ValidatorInterface` without custom validation layers.
- **Nested DTO Support**
    Supports complex request payloads with nested DTOs for query, body, header, file and route parameters.
- **Strict Typing Friendly**
    DTO properties can be strictly typed for better IDE support and safer refactoring.
- **Flexible Query Parameter Transformation**
    Query parameters can be transformed to scalar types or via custom callbacks.

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

[](#installation)

```
composer require crtl/request-dto-resolver-bundle
```

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

[](#configuration)

Register the bundle in your Symfony application:

```
// config/bundles.php
return [
    // ...
    Crtl\RequestDtoResolverBundle\CrtlRequestDtoResolverBundle::class => ["all" => true],
];
```

### Bundle Options

[](#bundle-options)

The bundle exposes two options under the `crtl_request_dto_resolver` key:

```
# config/packages/crtl_request_dto_resolver.yaml
crtl_request_dto_resolver:
    default_strict: true   # default: true
    default_null: false     # default: false
```

OptionTypeDefaultDescription`default_strict``bool``true`Controls how property values are assigned during hydration. When `true`, values are assigned directly (`$object->prop = $value`), which enforces PHP's native type checks. When `false`, values are assigned via reflection, allowing implicit type coercion.`default_null``bool``false`When `true`, properties that are missing from the request are treated as if `null` was sent. For non-nullable properties this produces a type constraint violation; for nullable properties the value is skipped as usual.These serve as **bundle-level defaults**. Individual DTOs can override them via the `#[RequestDto]` attribute:

```
// Uses bundle defaults for both options
#[RequestDto]
class MyDto { /* ... */ }

// Overrides strict for this DTO only
#[RequestDto(strict: false)]
class NonStrictDto { /* ... */ }

// Overrides defaultNull for this DTO only
#[RequestDto(defaultNull: true)]
class RequireAllFieldsDto { /* ... */ }
```

When the attribute parameter is omitted (or explicitly set to `null`), the bundle-level default is used.

Usage
-----

[](#usage)

### Step 1: Define a Request DTO

[](#step-1-define-a-request-dto)

Create a DTO class and annotate it with `#[RequestDto]`. Use parameter attributes to map request data to properties.

> **The attribute is required to identify which controller arguments should be resolved and validated.**

#### 1.1 Strictly typed DTO

[](#11-strictly-typed-dto)

```
namespace App\DTO;

use Crtl\RequestDtoResolverBundle\Attribute\BodyParam;
use Crtl\RequestDtoResolverBundle\Attribute\FileParam;
use Crtl\RequestDtoResolverBundle\Attribute\HeaderParam;
use Crtl\RequestDtoResolverBundle\Attribute\QueryParam;
use Crtl\RequestDtoResolverBundle\Attribute\RouteParam;
use Crtl\RequestDtoResolverBundle\Attribute\RequestDto;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Constraints as Assert;

#[RequestDto]
class ExampleDto
{
    #[BodyParam, Assert\NotBlank, Assert\Type("string")]
    public string $someParam;

     #[BodyParam, Assert\NotBlank, Assert\Type("string")]
    public string $withDefaultValue = "My default value";

    #[BodyParam]
    public ?string $nullable;

    #[FileParam, Assert\NotNull]
    public ?UploadedFile $file;

    #[HeaderParam("Content-Type"), Assert\NotBlank]
    public string $contentType;

    // Because query params are all strings by default
    // we can provide a type transformer to transform its type.
    // values are converted using filter_var with the corrosponding FILTER_VALIDATE_* option.
    #[QueryParam(transformType: "int"), Assert\GreaterThan(18)]
    public int $age;

    // Or provide a custom callable to tranform type
    #[
        QueryParam(
            name: "age",
            transformType: fn(string $value) => strtolower($value)
        ),
        Assert\GreaterThan(18)
    ]
    public mixed $customQueryParam;

    #[RouteParam, Assert\NotBlank]
    public string $id;

    // Nested DTOs are supported for BodyParam and QueryParam
    // Do NOT use Assert\Valid here
    #[BodyParam("nested")]
    public ?NestedRequestDTO $nestedBodyDto;

    // Optional constructor receiving the Request
    // It is recommended to make the request argument nullable to support creation of DTOs from
    // array data but not required when only used in HTTP contexts.
    public function __construct(?Request $request = null)
    {
    }
}
```

> **Any type mismatches will trigger a constraint violation and thus a `RequestValidationException` is thrown.**

#### 1.2 Mixed typed DTO

[](#12-mixed-typed-dto)

```
namespace App\DTO;

use Crtl\RequestDtoResolverBundle\Attribute\BodyParam;
use Crtl\RequestDtoResolverBundle\Attribute\FileParam;
use Crtl\RequestDtoResolverBundle\Attribute\HeaderParam;
use Crtl\RequestDtoResolverBundle\Attribute\QueryParam;
use Crtl\RequestDtoResolverBundle\Attribute\RouteParam;
use Crtl\RequestDtoResolverBundle\Attribute\RequestDto;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Constraints as Assert;

#[RequestDto]
class ExampleDto
{
    /**
     * @var string
     */
    #[BodyParam, Assert\NotBlank]
    public mixed $contentType;
}
```

### 1.3 Important notes about DTO hydration

[](#13-important-notes-about-dto-hydration)

- **Uninitialized properties**
    If a request does not contain data for a property, that property will remain uninitialized.
    To guarantee initialization, either:

    - provide a default value, or
    - add appropriate validation constraints (e.g. `NotNull`, `NotBlank`).
- **Hydration from arrays**
    When a DTO is manually hydrated from an array using `RequestDtoFactory::fromArray()`, the configured `AbstractParam::$name` is ignored.
    In this case, the DTO’s property names are always used as the source keys.
- **Validation still runs in strict mode**
    Even if some properties cannot be assigned due to type mismatches in strict typing mode, the DTO is still fully validated using Symfony’s validator.
    Any resulting violations will lead to a `RequestValidationException`.

### 1.4 Validation group sequences

[](#14-validation-group-sequences)

All Symfony validation group sequence variants are supported.

Because request data can never be trusted, **DTO properties may be uninitialized regardless of whether strict typing is used or not**.
Missing or invalid input can prevent a property from being assigned during hydration.

When using validation group sequences, you must therefore ensure that properties are accessed safely by:

- checking initialization with `isset()`, or
- using reflection-based checks when necessary.

Failing to do so may lead to runtime errors before later validation groups are evaluated.

### Step 2: Use the DTO in a Controller

[](#step-2-use-the-dto-in-a-controller)

```
namespace App\Controller;

use App\DTO\ExampleDto;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ExampleController extends AbstractController
{
    #[Route("/example", name: "example")]
    public function exampleAction(ExampleDto $data): Response
    {
        return new Response("DTO received and validated successfully!");
    }
}
```

---

### Handling Validation Errors

[](#handling-validation-errors)

On validation failure, a `RequestValidationException` is thrown.

> **The bundle registers a default exception subscriber (priority -32) that returns a `400 Bad Request` JSON response.**

You can override this with your own listener:

```
namespace App\EventListener;

use Crtl\RequestDtoResolverBundle\Exception\RequestValidationException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class RequestValidationExceptionListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::EXCEPTION => ["onKernelException", 0],
        ];
    }

    public function onKernelException(ExceptionEvent $event): void
    {
        $exception = $event->getThrowable();

        if ($exception instanceof RequestValidationException) {
            $event->setResponse(new JsonResponse([
                "error" => "Validation failed",
                "details" => $exception->getViolations(),
            ], JsonResponse::HTTP_BAD_REQUEST));
        }
    }
}
```

License
-------

[](#license)

This bundle is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance62

Regular maintenance activity

Popularity16

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity61

Established project with proven stability

 Bus Factor1

Top contributor holds 82% of commits — single point of failure

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 ~44 days

Recently: every ~2 days

Total

15

Last Release

96d ago

Major Versions

0.0.3 → v2.0.12026-02-01

v2.0.2 → 3.x-dev2026-02-02

v2.2.0 → v3.0.02026-02-09

PHP version history (2 changes)0.0.1PHP &gt;=8.0

0.0.2PHP &gt;=8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/e5628d83cd6431df9b6f835e6ac7ce9438ddf1fc6871af1b17d53b4d7f80826c?d=identicon)[crtl](/maintainers/crtl)

---

Top Contributors

[![crtl](https://avatars.githubusercontent.com/u/25827827?v=4)](https://github.com/crtl "crtl (50 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (11 commits)")

---

Tags

phpphp8symfonysymfony-bundle

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[sylius/sylius

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

8.4k5.6M651](/packages/sylius-sylius)[nelmio/api-doc-bundle

Generates documentation for your REST API from attributes

2.3k63.6M233](/packages/nelmio-api-doc-bundle)[sulu/sulu

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

1.3k1.3M152](/packages/sulu-sulu)[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)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

595.2M386](/packages/shopware-core)

PHPackages © 2026

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