PHPackages                             sirix/mezzio-valinor-request-mapper - 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. [HTTP &amp; Networking](/categories/http)
4. /
5. sirix/mezzio-valinor-request-mapper

ActiveLibrary[HTTP &amp; Networking](/categories/http)

sirix/mezzio-valinor-request-mapper
===================================

Transparently maps PSR-7 requests to typed DTOs via cuyz/valinor in Mezzio applications

1.0.0(1mo ago)04361MITPHPPHP ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0CI passing

Since May 9Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/sirix777/mezzio-valinor-request-mapper)[ Packagist](https://packagist.org/packages/sirix/mezzio-valinor-request-mapper)[ Fund](https://buymeacoffee.com/sirix)[ GitHub Sponsors](https://github.com/sirix777)[ RSS](/packages/sirix-mezzio-valinor-request-mapper/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (3)Dependencies (21)Versions (5)Used By (1)

mezzio-valinor-request-mapper
=============================

[](#mezzio-valinor-request-mapper)

[![Latest Stable Version](https://camo.githubusercontent.com/856741432d14ff51d3fd7c289129a95e8df5046fea574988e27cf46e0354efa2/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d76616c696e6f722d726571756573742d6d61707065722f76)](https://packagist.org/packages/sirix/mezzio-valinor-request-mapper) [![Total Downloads](https://camo.githubusercontent.com/a53f2049bbafbaa7177c3739a60f5ec4589579137eff073ec9d5e3195da5f4e9/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d76616c696e6f722d726571756573742d6d61707065722f646f776e6c6f616473)](https://packagist.org/packages/sirix/mezzio-valinor-request-mapper) [![Latest Unstable Version](https://camo.githubusercontent.com/09dbf3ddd720f78d445fa0f06a0c4eb56d696ce8830c0addc786a019c0fd1460/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d76616c696e6f722d726571756573742d6d61707065722f762f756e737461626c65)](https://packagist.org/packages/sirix/mezzio-valinor-request-mapper) [![License](https://camo.githubusercontent.com/db867504768f18e258c0315fec932391f1b113c7818bbbcfb438ce9427fc9d00/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d76616c696e6f722d726571756573742d6d61707065722f6c6963656e7365)](https://packagist.org/packages/sirix/mezzio-valinor-request-mapper) [![PHP Version Require](https://camo.githubusercontent.com/0e55ac241b20df8393c2d6a2a57a9f4e445b14ba24441dc864191f174808e99b/687474703a2f2f706f7365722e707567782e6f72672f73697269782f6d657a7a696f2d76616c696e6f722d726571756573742d6d61707065722f726571756972652f706870)](https://packagist.org/packages/sirix/mezzio-valinor-request-mapper)

Typed request mapping for Mezzio handlers via `cuyz/valinor`.

This package reads `#[MapRequest]` attributes on route handlers and maps request input (body/query/route) into DTOs before your handler code runs.

Features
--------

[](#features)

- `#[MapRequest]` attribute for class and method targets (repeatable)
- Mapping from:
    - parsed body (`body`)
    - query params (`query`)
    - route params (`route`)
    - combined HTTP request (`source`) using Valinor HTTP attributes (`FromBody`, `FromQuery`, `FromRoute`)
- Optional request attribute key override via `output`
- HTTP method filter via `methods` (case-insensitive, normalized to uppercase)
- JSON error responses on mapping failures
- Optional Valinor error message remapping (`message_map`)

Requirements
------------

[](#requirements)

- PHP `~8.2 || ~8.3 || ~8.4 || ~8.5`
- `cuyz/valinor ^2.0`
- `laminas/laminas-stratigility ^4.3`
- `mezzio/mezzio ^3.20`
- `mezzio/mezzio-router ^3.15 || ^4.1`
- `sirix/mezzio-routing-contracts ^1.0`

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

[](#installation)

```
composer require sirix/mezzio-valinor-request-mapper
```

The package auto-registers its config provider via Composer extra config. If your app does not use `laminas-config-aggregator`, add manually:

```
\Sirix\Mezzio\Valinor\ConfigProvider::class,
```

Middleware registration modes
-----------------------------

[](#middleware-registration-modes)

### 1) Standalone Mezzio (without `sirix/mezzio-routing-attributes`)

[](#1-standalone-mezzio-without-sirixmezzio-routing-attributes)

Register middleware globally after route matching and before dispatch:

```
$app->pipe(\Mezzio\Router\Middleware\RouteMiddleware::class);
$app->pipe(\Sirix\Mezzio\Valinor\Middleware\ValinorRequestMapperMiddleware::class);
$app->pipe(\Mezzio\Router\Middleware\DispatchMiddleware::class);
```

In standalone mode the middleware resolves `#[MapRequest]` by reflection from:

- class-level attributes on the matched route handler
- method-level attributes on PSR-15 `process()`
- method-level attributes on request handler `handle()` when Mezzio wraps it as route middleware
- method-level attributes on invokable route middleware via `__invoke()`

### 2) With `sirix/mezzio-routing-attributes`

[](#2-with-sirixmezzio-routing-attributes)

If your app uses `sirix/mezzio-routing-attributes` and it scans/collects route attribute modifiers, `MapRequest` is discovered as a `RouteAttributeModifierInterface` implementation and `ValinorRequestMapperMiddleware` is attached to matching routes automatically.

In this mode you usually do **not** need to register `\Sirix\Mezzio\Valinor\Middleware\ValinorRequestMapperMiddleware::class`as a global pipeline middleware.

Example (class-level + method-level attributes):

```
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Sirix\Mezzio\Routing\Attributes\Attribute\Get;
use Sirix\Mezzio\Routing\Attributes\Attribute\Post;
use Sirix\Mezzio\Valinor\Attribute\MapRequest;

final readonly class PaginationRequest
{
    public function __construct(public int $page = 1) {}
}

final readonly class CreateOrderRequest
{
    public function __construct(public string $name, public string $email) {}
}

#[MapRequest(query: PaginationRequest::class)]
final class OrdersHandler
{
    #[Get('/orders', name: 'orders.list')]
    public function list(ServerRequestInterface $request): ResponseInterface
    {
        $pagination = $request->getAttribute(PaginationRequest::class);
        // ...
    }

    #[Post('/orders', name: 'orders.create')]
    #[MapRequest(body: CreateOrderRequest::class, output: 'form')]
    public function create(ServerRequestInterface $request): ResponseInterface
    {
        $form = $request->getAttribute('form');
        // ...
    }
}
```

In this setup `#[MapRequest]` contributes route middleware via routing attribute processing, so no extra global pipeline registration is required for the mapper middleware.

Quick start
-----------

[](#quick-start)

```
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Sirix\Mezzio\Valinor\Attribute\MapRequest;

final readonly class CreateUserRequest
{
    public function __construct(
        public string $name,
        public string $email,
    ) {}
}

#[MapRequest(body: CreateUserRequest::class)]
final class CreateUserHandler implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        /** @var CreateUserRequest $dto */
        $dto = $request->getAttribute(CreateUserRequest::class);

        // use $dto ...
    }
}
```

Attribute API
-------------

[](#attribute-api)

```
new MapRequest(
    body: ?string,   // class-string DTO from parsed body
    query: ?string,  // class-string DTO from query params
    route: ?string,  // class-string DTO from route params
    source: ?string, // class-string DTO from combined request sources
    output: ?string, // request attribute key, defaults to DTO FQCN
    methods: array,  // HTTP methods filter
);
```

Rules:

- `source` is mutually exclusive with `body/query/route`
- if `output` is omitted, mapped DTO is stored under its class name
- `methods = []` means any HTTP method
- `methods` are normalized (`post`, `Post` -&gt; `POST`)
- if multiple `#[MapRequest]` attributes match current method, all of them are applied in declaration order; class-level mappings run before method-level mappings

Combined mapping (`source`)
---------------------------

[](#combined-mapping-source)

Use Valinor HTTP source attributes in DTO constructor:

```
use CuyZ\Valinor\Mapper\Http\FromBody;
use CuyZ\Valinor\Mapper\Http\FromQuery;
use CuyZ\Valinor\Mapper\Http\FromRoute;

final readonly class SearchRequest
{
    public function __construct(
        #[FromRoute] public string $locale,
        #[FromQuery] public string $q,
        #[FromBody] public ?array $filters = null,
    ) {}
}

#[MapRequest(source: SearchRequest::class)]
final class SearchHandler implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        $dto = $request->getAttribute(SearchRequest::class);
        // ...
    }
}
```

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

[](#configuration)

Create `config/autoload/mezzio-valinor.global.php`:

```
