symfony: Collection validation does not stop after first violation

PHP 7 Using Symfony\Component\Validator\Constraints\Collection;

I’m using Symfony 3.1 and I hope I’m not doing something wrong. When I validate a field, it doesn’t stop after the first violation. Example :

$errors = $val->validate(
    $info,
    new \Symfony\Component\Validator\Constraints\Collection([
        'fields' => array(
            'specialKey' => array(
                new NotBlank(),
                new Length(['min' => 8,'max' => 20]),
                new Regex(['value' => "/^[A-Za-z]*$/"]),
            )
        ),
        'allowExtraFields' => true,
    ])
);

I’m getting :

'This value is too short. It should have 8 characters or more.'
'This value is not valid.'

I found a walk-around for those who are interested :

$errors = $val->validate(
    $info,
    new \Symfony\Component\Validator\Constraints\Collection([
        'fields' => array(
            'specialKey' => array(
                new NotBlank(['groups' => 'groupe1']),
                new Length(['min' => 8,'max' => 20,'groups' => 'groupe2']),
                new Regex(['value' => "/^[A-Za-z]*$/",'groups' => 'groupe3']),
            )
        ),
        'allowExtraFields' => true,
    ]),
    new GroupSequence(array('Default','groupe1','groupe2','groupe3'))
);

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 24 (16 by maintainers)

Commits related to this issue

Most upvoted comments

I agree with @jaikdean. If I want to check a property for non-empty string and then second validator checks it in the database, the query will be executed anyway, even if the first validator will fail, and that’s not cool.

I agree with @xabbuh here, and group sequences already are a solution to this. But, throwing a raw idea: could a @Sequence constraint be helpful? It’ll be a composite constraint and accept a collection of constraints as argument (like the @All constraint for instance), and proceed to the next constraint validation only if there is no violation yet. Looks like it could help solving some simple cases straightforwardly to me (but not as powerful as group sequences).

+1 for a “stopOnFailure” type option.

Our use case is using validators to check the format of user input from a JSON request body. Say we’ve decoded the JSON and want a PIN field to be a string of 4 numeric digits (not an integer in case it starts with a 0):

$constraints = [
    new Type('string'),
    new Regex('^\d{4}$'),
];

$validator->validate($pin, $constraints);

This all appears to work correctly, until the user starts messing around and sends an array for the value of this field. In this case, the Type constraint causes a violation as expected, but the Regex constraint throws an uncaught UnexpectedTypeException.

We’re currently calling validate() for each constraint individually and stopping when we get a violation, to work around this.

@pfcloutier I tagged this as a feature request.

I’m looking for pretty much what you describe @ogizanagi so yes, that sounds like a good idea to me.

If I want to check a property for non-empty string and then second validator checks it in the database, the query will be executed anyway, even if the first validator will fail, and that’s not cool.

For that use case there is the concept of group sequence providers where subsequent validation groups are only checked if the previous ones did not error.

I see many people around suggesting like @xabbuh and @ogizanagi using group sequences, but this may not always be a good workaround as a violation in one group would skip validation of constraints in subsequent groups on all other fields as well, even if those fields comply with first groups constraints but violate some constraints in subsequent groups.

Although I understand @Jean85’s POV and the need to show all independent constraint violations at once (like password length and charset), some constraints may still be dependent on previous ones being valid.

I think that’s especially true about data type constraints, as @jaikdean and @rijnhard pointed out, since trying to validate constraints on a value of the wrong type makes no sense to me (e.g. why raising an error about a date field not being in an expected period of time if the input is not a date in the first place?), and this may even lead to raising exceptions/errors if subsequent validation rely on code that assumes that values have the expected type. I’m facing a similar issue validating a field which is expected to be a float value (so having a Type constraint on it) and also having Expression constraints using some arithmetics with the value. If the input is not numeric, then the evaluation of these expressions raises an ErrorException up to the validate() method invocation thus the constraint violation list of the whole entity is lost and subsequent fields aren’t validated either! If I want to avoid it, I can still duplicate type checking in every subsequent constraint (using ternary operator around value occurrences in my case) but this is dirty IMO, this clutters the code and makes less clear what the actual constraint is.

As an API user, I think we should have an easy way to set a Type constraint and use it as a safe guard before further validation, without having to write a custom validator or such. Moreover, I feel this is inconsistent with how validators deal with null values. As @webmozart said in #10221 comment:

The reason why most of the validators silently allow null values is that (a) whether a value is set or not and (b) which constraints this value has to satisfy, if it is set, are usually orthogonal concepts.

Then why not silently allowing incoherent data types as well? I feel like (a) whether a value’s type is coherent/safe and (b) which constraints this value has to satisfy, if it is coherent/safe type-wise, are also orthogonal concepts. And this may be why some (many?) of us don’t expect Symfony to raise further violations when type constraint is violated.

+1 for a @Sequence compound constraint (although it may make constraint inference more difficult for other annotation-scanning components?) +1 as well for a “stopOnFailure” (or “orSkipNext”) option, at least on Type constraint but maybe even a base inherited option, defaulted to no-skip to preserve BC. Something like:

/**
 * @Assert\Type("float", orSkipNext=true)
 * @Assert\Expression(...)
 */

(Apologies for being beyond OP scope about Collection validation of independent constraints, I didn’t want to create a new issue…)

How to Sequentially Apply Validation Groups

https://symfony.com/doc/3.4/validation/sequence_provider.html

works for me.

@iltar I know, but since the issue that you raised is UX related, that is helpful, since it stops the user even before submitting the form, requiring the field to be filled.

Why it should stop? If there are multiple errors, the users should be made aware of all of them! Otherwise it could get frustrating for him, getting a different error for 2-3 submission in a row