How beautifully to stop object property validation on the first error in Symfony?

Viewed 1148

I have the following code:


    class User
    {
        /**
         * @Assert\Type(type="string")
         * @Assert\NotBlank()
         * @Assert\Email()
         * @Assert\Length(max=255)
         */
        public $email;
    }

This object is filled from an API call. When validation takes place and property is filled with array value instead of string then NotBlank, Email, and Length validations continuing to work and I get "UnexpectedTypeException".

I want validation system just to add one error about wrong value type and stop there.

I've made custom constraint validator


    class ChainConstraintValidator extends ConstraintValidator
    {
        /**
         * {@inheritdoc}
         */
        public function validate($value, Constraint $constraint)
        {
            if (!$constraint instanceof ChainConstraint) {
                throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\All');
            }

            $context = $this->context;
            $prevCount = $context->getViolations()->count();
            $validator = $context->getValidator()->inContext($context);

            foreach ($constraint->constraints as $constraintStep) {
                $errors = $validator->validate($value, $constraintStep);

                if ($errors->getViolations()->count() > $prevCount) {
                    break;
                }
            }
        }
    }

It works and I used it like this:


    @ChainConstraint(
        @Assert\Type(type="string"),
        @Assert\NotBlank(),
        @Assert\Email(),
        @Assert\Length(max=255)
    )
    

I have a lot of such classes in my project. Is there any more beautiful and requiring less code a way to achieve this?

3 Answers

What about this :

$var = [];

$constraints = [
 new Type(['type'=>'string', 'groups'=> 'first-check']),
 new NotBlank(['groups'=> 'second-check']),
 new Email(['groups'=> 'second-check']),
 new Length(['min'=>2, 'groups'=> 'second-check'])]
    ;

$groups = ['first-check', 'second-check'];
$groupSequence = new GroupSequence($groups);
$error = $validator->validate($var, $constraints, $groupSequence);

if(count($error) > 0) {
    dd($error); // Contains 1 ConstraintViolation (Type) 
 }

Starting from Symfony 5.1 a Sequentially constraint is available.

This constraint allows you to apply a set of rules that should be validated step-by-step, allowing to interrupt the validation once the first violation is raised.

Related