sirix/mezzio-valinor-request-mapper 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

sirix/mezzio-valinor-request-mapper

Composer 安装命令:

composer require sirix/mezzio-valinor-request-mapper

包简介

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

README 文档

README

Latest Stable Version Total Downloads Latest Unstable Version License PHP Version Require

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

  • #[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

  • 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

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

1) Standalone Mezzio (without sirix/mezzio-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

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

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

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 -> 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)

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

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

<?php

declare(strict_types=1);

return [
    'sirix_mezzio_valinor' => [
        'mapper' => [
            'cache_dir' => __DIR__ . '/../../cache/valinor',
            'cache_watch' => false,
            'configurators' => [
                \CuyZ\Valinor\Mapper\Configurator\ConvertKeysToCamelCase::class,
            ],
            'allow_superfluous_keys' => true,
            'allow_scalar_value_casting' => true,
            'allow_permissive_types' => false,
            'allow_undefined_values' => false,
            'support_date_formats' => ['Y-m-d', 'd/m/Y'],
        ],
        'error' => [
            'status_code' => 422,
            'key_case' => null, // null|'snake_case'
            'message_map' => [
                // 'Value {source_value} is not a valid string.' => 'This field is required.',
            ],
        ],
    ],
];

Mapper options

Option Type Default Description
cache_dir ?string null Path to cache directory. When set, Valinor caches compiled type metadata via FileSystemCache
cache_watch bool false Wrap cache with FileWatchingCache to auto-invalidate when PHP files change (use in dev)
configurators array<string|MapperBuilderConfigurator> [] Services or class-strings applied via configureWith()
allow_superfluous_keys bool true Allow extra keys in input that are not mapped
allow_scalar_value_casting bool true Allow automatic scalar type casting (e.g. intstring)
allow_permissive_types bool false Allow mixed type to accept any value
allow_undefined_values bool false Fill missing keys with null instead of failing
support_date_formats list<string> [] Additional date formats for DateTimeInterface mapping

Cache

When cache_dir is set, Valinor caches compiled reflection data for mapped DTO types, significantly reducing first-request latency.

  • Production: set cache_dir and leave cache_watch disabled (default)
  • Development: set cache_watch: true so cache invalidates automatically when PHP files change

To pre-warm the cache during deployment, use a CLI script:

$mapperBuilder = (new \CuyZ\Valinor\MapperBuilder())
    ->withCache(new \CuyZ\Valinor\Cache\FileSystemCache('path/to/cache-dir'));

$mapperBuilder->warmupCacheFor(
    \App\Domain\CreateUserRequest::class,
    \App\Domain\PaginationRequest::class,
    // ...
);

Mapper configurators

mapper.configurators supports:

  • service id (resolved from container)
  • class-string implementing MapperBuilderConfigurator (instantiated if service not found)

Error options

Option Type Default Description
status_code int 422 HTTP status code for mapping error responses
key_case null|'snake_case' null Transform error path keys to snake_case
message_map array<string, string> [] Remap Valinor error messages by pattern match

Error response format

On mapping failure middleware returns JSON:

{
  "error": "Mapping failed",
  "messages": {
    "field": ["...message..."]
  }
}

Status code defaults to 422 and can be overridden by sirix_mezzio_valinor.error.status_code.

Notes and caveats

  • Middleware requires RouteResult attribute (it is a no-op when route is not matched yet).
  • With sirix/mezzio-routing-attributes, middleware can be added per-route automatically via attribute scanning.
  • For body mapping, malformed body structures can still fail at Valinor level and return configured mapping error response.
  • When using a custom output, ensure downstream code reads the same request key.

Release checklist

Before tagging a stable release:

composer validate --strict
composer normalize --dry-run --diff
composer analyse-deps
composer check

sirix/mezzio-valinor-request-mapper 适用场景与选型建议

sirix/mezzio-valinor-request-mapper 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 436 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 05 月 09 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「middleware」 「attributes」 「request」 「mapper」 「dto」 「hydration」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 sirix/mezzio-valinor-request-mapper 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 sirix/mezzio-valinor-request-mapper 我们能提供哪些服务?
定制开发 / 二次开发

基于 sirix/mezzio-valinor-request-mapper 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 436
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 42
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-09