codingculture/request-resolver-bundle
Installation
composer require codingculture/request-resolver-bundle
Ensure CodingCulture\RequestResolverBundle\RequestResolverBundle is enabled in config/bundles.php.
First Use Case: Basic Request Validation
Create a request class implementing ResolvableRequestInterface:
use CodingCulture\RequestResolverBundle\Request\ResolvableRequestInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class CreateUserRequest implements ResolvableRequestInterface
{
private $options = [];
public function getName(): string
{
return $this->options['name'];
}
public function defineOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'name' => null,
])
->setRequired(['name'])
->setAllowedTypes('name', 'string');
}
}
Resolve in Controller Inject and use the resolver:
use CodingCulture\RequestResolverBundle\Resolver\RequestResolver;
use Symfony\Component\HttpFoundation\Request;
class UserController extends AbstractController
{
public function create(RequestResolver $resolver, Request $request)
{
$userRequest = $resolver->resolve(new CreateUserRequest(), $request->request->all());
$name = $userRequest->getName(); // Safe access after validation
}
}
Request Validation in Controllers Replace manual validation with resolved requests:
public function update(RequestResolver $resolver, Request $request, int $id)
{
$updateRequest = $resolver->resolve(new UpdateUserRequest(), $request->all());
$user = $this->userRepository->find($id);
$user->update($updateRequest->getName(), $updateRequest->getEmail());
}
Nested Requests For complex payloads, nest requests:
final class CreateOrderRequest implements ResolvableRequestInterface
{
public function defineOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'items' => [],
])
->setAllowedTypes('items', 'array')
->setNormalizer('items', function ($items) {
return array_map(fn ($item) => $resolver->resolve(new OrderItemRequest(), $item), $items);
});
}
}
Service Integration Pass resolved requests to services:
public function checkout(RequestResolver $resolver, Request $request)
{
$checkoutRequest = $resolver->resolve(new CheckoutRequest(), $request->all());
$this->checkoutService->process($checkoutRequest);
}
$form = $this->createForm(CreateUserType::class);
$form->submit($request->request->all());
if ($form->isSubmitted() && $form->isValid()) {
$userRequest = $resolver->resolve(new CreateUserRequest(), $form->getData());
}
$apiRequest = $resolver->resolve(new ApiRequest(), json_decode($request->getContent(), true));
Type Safety
setNormalizer or setAllowedTypes explicitly:
$resolver->setAllowedTypes('age', 'int')
->setNormalizer('age', fn ($value) => (int) $value);
Circular References
setDefault with callables for lazy resolution:
$resolver->setDefaults([
'parent' => fn ($options) => $resolver->resolve(new ParentRequest(), $options['parent_data']),
]);
Error Handling
InvalidArgumentException on invalid data. Catch and handle gracefully:
try {
$request = $resolver->resolve(new SomeRequest(), $data);
} catch (InvalidArgumentException $e) {
return new JsonResponse(['error' => $e->getMessage()], 400);
}
defineOptions:
$resolver->setDefaults(function (OptionsResolver $r) {
\Log::debug('Resolver defaults:', $r->getDefaults());
});
Custom Resolver
Extend RequestResolver for global logic:
class CustomResolver extends RequestResolver
{
protected function configureOptions(OptionsResolver $resolver): void
{
$resolver->setNormalizer('timestamp', fn ($value) => strtotime($value));
}
}
Bind in services.yaml:
services:
CodingCulture\RequestResolverBundle\Resolver\RequestResolver: '@app.custom_resolver'
Dynamic Option Resolution
Use setNormalizer for runtime transformations:
$resolver->setNormalizer('user', fn ($id) => $this->userRepository->find($id));
Validation Groups Leverage Symfony Validator for complex rules:
use Symfony\Component\Validator\Constraints as Assert;
$resolver->setDefaults([
'email' => null,
])
->setAllowedTypes('email', ['string', 'null'])
->setNormalizer('email', fn ($email) => $this->validator->validate($email, [
new Assert\Email(),
new Assert\NotBlank(),
]));
How can I help you explore Laravel packages today?