digitalrevolution/symfony-request-validation
Installation:
composer require digitalrevolution/symfony-request-validation
Register the bundle in config/bundles.php:
DigitalRevolution\SymfonyRequestValidation\Bundle\RequestValidationBundle::class => ['all' => true],
First Use Case:
Create a request class extending AbstractValidatedRequest and define validation rules:
use DigitalRevolution\SymfonyRequestValidation\AbstractValidatedRequest;
use DigitalRevolution\SymfonyRequestValidation\ValidationRules;
class CreateUserRequest extends AbstractValidatedRequest
{
protected function getValidationRules(): array
{
return [
ValidationRules::string('name')->required()->maxLength(50),
ValidationRules::email('email')->required(),
ValidationRules::numeric('age')->min(18)->max(120),
];
}
}
Register as Service:
Tag the request class as a service in services.yaml:
services:
App\Request\CreateUserRequest:
tags: ['request_validation.request']
Inject and Validate:
use Symfony\Component\HttpFoundation\RequestStack;
class UserController
{
public function __construct(
private RequestStack $requestStack,
private CreateUserRequest $createUserRequest
) {}
public function create(Request $request): Response
{
$this->createUserRequest->validate($request);
// Proceed with validated data
}
}
Request Validation in Controllers:
public function update(Request $request, UpdateUserRequest $updateRequest): Response
{
$updateRequest->validate($request);
$data = $updateRequest->getValidatedData();
// Use $data directly
}
Reusable Validation Logic: Create base request classes for shared validation:
class BaseUserRequest extends AbstractValidatedRequest
{
protected function getValidationRules(): array
{
return [
ValidationRules::string('name')->required(),
ValidationRules::email('email')->required(),
];
}
}
Extend for specific use cases:
class UpdateUserRequest extends BaseUserRequest
{
protected function getValidationRules(): array
{
return array_merge(parent::getValidationRules(), [
ValidationRules::numeric('age')->min(18),
]);
}
}
Custom Error Handling:
Override handleValidationErrors() in your request class:
protected function handleValidationErrors(ConstraintViolationListInterface $errors): void
{
throw new HttpException(400, json_encode($errors));
}
Dynamic Rule Generation: Use closures for conditional rules:
protected function getValidationRules(): array
{
return [
ValidationRules::string('password')->required()->when(
fn() => $this->get('action') === 'create',
fn($rule) => $rule->minLength(8)
),
];
}
Integration with Symfony Forms: Use validated data directly in form factories:
public function create(Request $request, CreateUserRequest $createRequest): Response
{
$createRequest->validate($request);
$form = $this->createForm(UserType::class, $createRequest->getValidatedData());
}
Service Registration:
ServiceNotFoundException.tags: ['request_validation.request'] is present in services.yaml.Circular Dependencies:
CreateUserRequest injecting UpdateUserRequest) causes circular references.Request objects or validated data.Rule Overrides:
array_merge() to combine rules:
return array_merge(parent::getValidationRules(), [...]);
Performance with Large Payloads:
ValidationRules::skip() for non-critical fields:
ValidationRules::skip('metadata.non_critical_field')
Symfony Cache Invalidation:
php bin/console cache:clear after updating rules.Validation Errors:
dd($this->getValidationErrors()) to inspect raw errors during development.Rule Debugging:
config/packages/validator.yaml:
validator:
debug: true
Logging:
protected function handleValidationErrors(ConstraintViolationListInterface $errors): void
{
$this->logger->error('Validation failed', ['errors' => $errors]);
throw new HttpException(400, 'Validation failed');
}
Custom Validators:
use Symfony\Component\Validator\Constraint;
class CustomConstraint extends Constraint
{
public $message = 'This value is invalid.';
}
services.yaml:
services:
App\Validator\Constraints\CustomConstraint:
tags: [validator.constraint]
Event Listeners:
RequestValidationEvent):
use DigitalRevolution\SymfonyRequestValidation\Event\RequestValidationEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ValidationSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
RequestValidationEvent::VALIDATION_FAILED => 'onValidationFailed',
];
}
public function onValidationFailed(RequestValidationEvent $event): void
{
// Custom logic
}
}
Custom Error Formats:
formatErrors() to change the error response format:
protected function formatErrors(ConstraintViolationListInterface $errors): array
{
return array_map(fn($error) => [
'field' => $error->getPropertyPath(),
'message' => $error->getMessage(),
], iterator_to_array($errors));
}
Integration with API Platform:
denormalize() methods:
public function denormalize($data, string $type, array $context = []): User
{
$request = new CreateUserRequest();
$request->validate(new Request([], [], [], [], [], ['json' => $data]));
return new User($request->getValidatedData());
}
How can I help you explore Laravel packages today?