macpaw/request-dto-resolver
macpaw/request-dto-resolver automatically maps incoming Laravel request data into typed DTOs, cutting boilerplate and keeping controllers clean. It resolves DTO constructor parameters, supports nested objects, and makes request handling more explicit, predictable, and easy to test.
composer require macpaw/request-dto-resolver. Pin to ^1.1.2 (see Gotchas — v1.1.1 regressed attribute lookup).src/ for the exact bundle class name, then add it to config/bundles.php.README.md for the public API and the attributes the resolver reads.src/ for the argument resolver and bundle extension — specifically the attribute lookup logic touched by v1.1.2.tests/ for working examples — with 1 star and 0 dependents, tests are likely the most reliable documentation.$request->query->get(...) extraction in a controller with a typed DTO decorated with the resolver's attribute.final class SearchRequest
{
public function __construct(
public readonly string $q = '',
public readonly int $page = 1,
public readonly int $perPage = 20,
) {}
}
#[Route('/search', methods: ['GET'])]
public function search(#[MapRequestDto] SearchRequest $request): JsonResponse
{
// $request is populated from query parameters by the resolver
}
As of v1.1.2 the resolver performs route attribute lookup to decide how a DTO parameter is resolved. Do not rely on the bare controller type-hint alone: attach the package's attribute (or the route-level configuration it documents) and confirm in the README/tests which attribute name and target (parameter vs. class) is expected. Verify the attribute is picked up before adding validation or nested objects.
readonly properties make the resolved object predictable and safe to pass to services.final class CreateUserRequest
{
public function __construct(
#[Assert\NotBlank]
#[Assert\Email]
public readonly string $email,
#[Assert\Length(min: 8)]
public readonly string $password,
) {}
}
#[Route('/users', methods: ['POST'])]
public function create(#[MapRequestDto] CreateUserRequest $request): JsonResponse
{
// validated DTO ready for the application layer
}
DateTimeImmutable, check how the bundle uses the Symfony Serializer. You may need serializer normalizers, #[SerializedName], or custom denormalizers.#[MapRequestPayload] / #[MapQueryString] exist. Compare behaviour before adopting this package broadly; this bundle may be useful for query DTOs or older Symfony versions, but avoid running two competing resolvers without understanding priority.KernelBrowser functional tests to assert that query/body data reaches the controller as a populated DTO. Add a regression test that the attribute on the parameter is honoured — that is precisely the behaviour v1.1.2 restores.^1.1.2 and re-test hydration after any upgrade in this line.composer.json and review changelogs before upgrades.composer.json is the source of truth. Do not assume compatibility with older or newer Symfony releases.bin/console debug:container to confirm the resolver service is registered and inspect its priority.symfony/validator is installed and that the resolver actually validates the DTO. Without this, invalid payloads can reach your controller.ReflectionParameter::getAttributes().debug:event-dispatcher and debug:container to trace resolver registration.var/log/dev.log for serializer/validator exceptions.composer install, composer phpunit, and XDEBUG_MODE=coverage vendor/bin/phpunit for coverage. The repo does not commit composer.lock, so installs resolve fresh dependency versions.config/packages/. Prefer decorating or configuring these over forking the bundle.How can I help you explore Laravel packages today?