symfony/validator
Symfony Validator component validates values and objects using a constraint-based system inspired by JSR-303 Bean Validation. Supports built-in and custom constraints, rich violation messages, and integration with Symfony forms and frameworks.
Installation:
composer require symfony/validator
For Laravel, use symfony/validator alongside symfony/property-access (if not already included via symfony/http-foundation).
Basic Validation:
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints as Assert;
$validator = Validation::createValidator();
$value = 'test@example.com';
$constraint = new Assert\Email();
$violations = $validator->validate($value, $constraint);
if (count($violations) === 0) {
// Valid
}
First Use Case: Validate a form request in Laravel:
use Illuminate\Http\Request;
use Symfony\Component\Validator\Validator\ValidatorInterface;
public function __construct(ValidatorInterface $validator) {
$this->validator = $validator;
}
public function store(Request $request) {
$data = $request->validate([
'email' => ['required', 'email'],
'age' => ['integer', 'min' => 18, 'max' => 120],
]);
// Proceed with valid data
}
ValidatorInterface from symfony/validator via Laravel’s service container.Email, NotBlank, Length, Unique, Callback.Form/Request Validation:
use Symfony\Component\Validator\Constraints as Assert;
$constraints = new Assert\Collection([
'email' => new Assert\Email(),
'password' => new Assert\Length(['min' => 8]),
]);
$violations = $validator->validate($request->all(), $constraints);
Domain Model Validation:
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;
$metadata = new ClassMetadata(User::class);
$metadata->addPropertyConstraint('email', new Assert\Email());
$metadata->addPropertyConstraint('age', new Assert\Range(['min' => 18]));
$validator->validate($user, $metadata);
API Payload Validation:
$payload = json_decode($request->getContent(), true);
$constraints = new Assert\All([
new Assert\Type('array'),
new Assert\Callback(function ($item) {
return strlen($item) <= 255;
}),
]);
$validator->validate($payload, $constraints);
Laravel Forms: Use symfony/validator with Laravel’s FormRequest:
public function rules() {
return [
'email' => 'required|email',
'age' => 'integer|min:18',
];
}
public function withValidator($validator) {
$validator->addConstraintTo('email', new Assert\NotBlank());
}
Custom Constraints:
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
class CustomConstraint extends Constraint {
public $message = 'This value is invalid.';
}
Validation Groups:
$validator->validate($object, null, ['group1', 'group2']);
Dynamic Constraints:
$constraint = new Assert\Callback(function ($value) use ($dynamicRule) {
return $value === $dynamicRule;
});
Cascading Failures:
Use Assert\Valid to validate nested objects/collections recursively.
Performance:
Assert\All sparingly.$validator->validate($user, $metadata); // Reuse $metadata
Constraint Options:
GroupSequence or using implicit constraint names in YAML/XML.NotBlank may not work as expected with null values. Use Assert\NotNull or Assert\Blank explicitly.Custom Constraints:
configureOptions() in custom constraints validates required options:
public function configureOptions(OptionsResolver $resolver) {
$resolver->setRequired(['param']);
$resolver->setDefault('param', 'default');
}
Expression, ensure the ExpressionLanguage component is installed:
composer require symfony/expression-language
Edge Cases:
Assert\Regex with caution on untrusted data.DateTime limits (e.g., 1901-12-31 vs. 1970-01-01). Use Assert\GreaterThan/LessThan with care.Laravel-Specific:
Validator facade wraps symfony/validator. For advanced use, inject ValidatorInterface directly:
public function __construct(private ValidatorInterface $validator) {}
FormRequest uses symfony/validator under the hood. Override failedValidation() for custom responses:
public function failedValidation(Validator $validator) {
throw new \Exception('Custom error');
}
Violation Details:
foreach ($violations as $violation) {
echo $violation->getPropertyPath() . ': ' . $violation->getMessage();
}
Constraint Dumping:
Use ValidatorBuilder to inspect constraints:
$builder = Validation::createValidatorBuilder();
$validator = $builder->getValidator();
$metadata = $validator->getMetadataFactory()->getMetadataFor($class);
Constraint Order:
Validation stops at the first failure by default. Use Assert\Valid for nested validation or Validator::validate($value, $constraints, null, ['stop_on_first_error' => false]).
Custom Validators:
use Symfony\Component\Validator\ConstraintValidator;
class CustomValidator extends ConstraintValidator {
public function validate($value, Constraint $constraint) {
if (!preg_match($constraint->pattern, $value)) {
$this->context->buildViolation($constraint->message)
->addViolation();
}
}
}
Metadata Customization:
Override Loader\LoaderInterface to load constraints from custom sources (e.g., database).
Validation Groups: Dynamically assign groups based on context:
$validator->validate($object, null, ['group_' . $user->role]);
Constraint Composition:
Combine constraints using Assert\All, Assert\Collection, or Assert\Valid for complex rules.
Event Listeners:
Attach listeners to validation events (e.g., ConstraintViolatedEvent):
$validator->addListener('constraint_violated', function ($event) {
// Log or transform violations
});
How can I help you explore Laravel packages today?