symfony/validator
Symfony Validator component validates values and object graphs using JSR-303 Bean Validation rules. Includes rich constraints, constraint validators, groups, custom constraints, and internationalized error messages. Integrates with forms and frameworks.
Installation:
composer require symfony/validator:^8.1
For Laravel, ensure compatibility with Symfony 8.1 (Laravel 10+ recommended). Use symfony/validator alongside symfony/options-resolver.
First Use Case: Validate a simple form input (e.g., email) in a Laravel controller or Form Request:
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Constraints as Assert;
// In your controller or service
$validator = app(ValidatorInterface::class);
$value = 'test@example.com';
$constraint = new Assert\Email();
$errors = $validator->validate($value, $constraint);
if (count($errors) > 0) {
// Handle validation errors
}
Key Entry Points:
Email, Length, NotBlank).default, create, update).findByCodes(): New method in ConstraintViolationListInterface (v8.1.0-BETA2) to filter violations by error codes.Laravel Integration:
Validator::make() (Laravel’s facade) for quick validation:
use Illuminate\Support\Facades\Validator;
$validator = Validator::make($request->all(), [
'email' => ['required', 'email'],
'password' => ['required', 'min:8'],
]);
Documentation: Start with Symfony’s Validator Component Docs and focus on:
findByCodes()).use Illuminate\Foundation\Http\FormRequest;
use Symfony\Component\Validator\Constraints as Assert;
class StoreUserRequest extends FormRequest
{
public function rules()
{
return [
'email' => ['required', 'email'],
'age' => ['integer', new Assert\GreaterThan(18)],
];
}
public function withValidator($validator)
{
$validator->addConstraintTo('email', new Assert\NotBlank());
}
}
$validator = app(ValidatorInterface::class);
$user = new User($request->input());
$errors = $validator->validate($user, null, ['create']); // 'create' is the validation group
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class ValidDomain extends Constraint
{
public $domain;
public $message = 'The domain "{{ domain }}" is not allowed.';
}
class ValidDomainValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if (!in_array($value, explode(',', $constraint->domain))) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ domain }}', $constraint->domain)
->addViolation();
}
}
}
$constraint = new ValidDomain(['domain' => 'example.com,test.com']);
$validator->validate($email, $constraint);
use Symfony\Component\Validator\Constraints as Assert;
class User
{
#[Assert\NotBlank(groups: ['create'])]
public $name;
#[Assert\Email(groups: ['update'])]
public $email;
}
$validator->validate($user, null, ['create']); // Only validates 'create' group
$validator->validate($user->address, null, ['default', 'address']);
AppServiceProvider:
Validator::extend('cascade', function () {
return function ($attribute, $value, $parameters, $validator) {
$validator->inContext()->atRoot()->validate($value);
return true;
};
});
$data = json_decode($request->getContent(), true);
$validator = Validator::make($data, [
'data.attributes.email' => ['required', 'email'],
]);
symfony/validator with webonyx/graphql-php or json-schema for complex APIs.use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected static function boot()
{
static::saving(function ($user) {
$validator = Validator::make($user->attributesToArray(), [
'email' => ['required', 'email'],
]);
if ($validator->fails()) {
throw new \Exception($validator->errors()->first());
}
});
}
}
findByCodes():
$violations = $validator->validate($object);
$emailViolations = $violations->findByCodes(['email', 'not_blank']);
foreach ($emailViolations as $violation) {
echo $violation->getMessage(); // "This value should not be blank."
}
Validator facade:
$validator = Validator::make($data, $rules);
$validator->validate();
$violations = $validator->getValidator()->getViolations();
$filtered = $violations->findByCodes(['custom_constraint']);
Leverage Laravel’s Facades:
Validator::make() or Validator::extend() over raw ValidatorInterface for Laravel projects.Validator::resolved() to access the underlying Symfony validator if needed.Form Request Validation:
FormRequest and override rules() or withValidator() for reusable validation logic.Custom Error Messages:
$constraint = new Assert\Length([
'min' => 8,
'minMessage' => 'Password must be at least {{ limit }} characters.',
]);
Validation in Services:
ValidatorInterface into services for domain-specific validation:
public function __construct(private ValidatorInterface $validator) {}
public function validateSubscription(Subscription $subscription)
{
$this->validator->validate($subscription, null, ['subscription']);
}
Testing Validations:
Validator facade in tests:
$validator = Validator::make($data, $rules);
$validator->validate(); // Throws exception on failure
$validator->validateResolved(); // Returns resolved data or throws
findByCodes():
$validator = Validator::make(['email' => ''], ['email' => 'not_blank']);
$validator->validate();
$violations = $validator->getValidator()->getViolations();
$this->assertCount(1, $violations->findByCodes(['not_blank']));
new Assert\Length(['min' => 8]) vs. new Assert\Length(8)).new Assert\Length(min: 8, max: 20)
GroupSequence (deprecated in SymfonyHow can I help you explore Laravel packages today?