8

Given this input:

[
    'key' => 'value',
]

How to validate to ensure that:

  1. key attribute exists
  2. Its value is an array (with any number of elements)

I expected this constraint to work

    $constraint = new Collection([
        'key' => new Required([
            new Type('array'),
            new Collection([
                'value' => new Required([
                    new NotBlank(),
                ]),
            ]),
        ]),
    ]);

but it throws an exception:

Symfony\Component\Validator\Exception\UnexpectedTypeException: Expected argument  of type "array or Traversable and ArrayAccess", "string" given

What am I missing?

PS: it's symfony v2.7.1

PPS: just to clarify: I know one can use a callback. If I wanted to re-implement the validation manually from scratch - I wouldn't have used symfony at the very first place. So the question is particularly about combining the existing constraints and not about using a callback constraint..

zerkms
  • 249,484
  • 69
  • 436
  • 539

3 Answers3

5

I had the exact same problem two nights ago.

The conclusion at the very end was that Symfony2 validation has no "fast-fail" validation. That is, even if your Type() constraint would fail it would proceed with other constraints and thus fail with UnexpectedTypeException exception.

However, I was able to find a way to tackle that:

$constraint = new Collection([
    'key' => new Required([
        new Type(['type' => 'array']),
        new Collection([
            // Need to wrap fields into this
            // in order to provide "groups"
            'fields' => [ 
                'value' => new Required([
                    new NotBlank(),
                ]),
            ],
            'groups' => 'phase2' // <-- THIS IS CRITICAL
        ]),
    ]),
]);

// In your controller, service, etc...
$V = $this->get('validator');

// Checks everything by `Collection` marked with special group
$violations = $V->validate($data, $constraint);

if ( $violations->count()){
    // Do something
}

// Checks *only* "phase2" group constraints
$violations = $V->validate($data, $constraint, 'phase2');

if ( $violations->count()){
    // Do something
}

Hope that this helps a bit. Personally, I find it annoying that we need to do this. Some sort of "fast-fail" flag within validator service would be much helpful.

Jovan Perovic
  • 19,846
  • 5
  • 44
  • 85
  • 1
    That is finally something that looks like an answer, thanks :-) 3 days left for bounty to expire, this one is the only candidate yet. – zerkms Jul 02 '15 at 20:37
2

You're saying the Collection constraint should just fail instead of throwing an exception because 'value' is a string and not an array.

There is a recently logged Symfony bug for this: https://github.com/symfony/symfony/issues/14943

Mark Achée
  • 517
  • 1
  • 4
  • 14
  • Constraints have a concept of so called "default options", which is `type` in case of a `Type` constraint. Which means that `new Type(['type' => 'array'])` is identical to `new Type('array')`. See https://github.com/symfony/symfony/blob/2.8/src/Symfony/Component/Validator/Constraints/Type.php#L32 and https://github.com/symfony/symfony/blob/2.8/src/Symfony/Component/Validator/Constraint.php#L144 – zerkms Jun 24 '15 at 22:14
  • Doing type validation is always "fun" because at what level the actual type error occurred is rarely clear. Sorry about that, try this new answer. – Mark Achée Jun 24 '15 at 22:39
  • "Your input has the string 'value' as the value of index 'key', which is not an array, so the exception is thrown here" --- I know why it was thrown. The question was: how to write a constraint that would accept **ANY INPUT**. If I could guarantee that input is always valid - I wouldn't use validators. "If you change your input to" --- would you literally put it on your site: please dear hackers, don't pass us what we not except since it crashes our application? – zerkms Jun 24 '15 at 22:55
1

Use Callback constraint(docs) where you can implement your custom validation logic.

The other way is to create custom constraint and validator classes. (docs)

Vadim Ashikhman
  • 9,851
  • 1
  • 35
  • 39
  • 1
    What exactly are you suggesting? To reimplement all the symfony2 constraints? What is the point of using a symfony validator at all then? – zerkms Jun 24 '15 at 06:42