How to validate if an element of an array is an array itself?
Asked Answered
S

3

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..

Spindle answered 24/6, 2015 at 5:22 Comment(2)
something like if ((is_array($a)) or ($a instanceof Traversable))Rinderpest
@Umair the question is particularly about using symfony2 validatorSpindle
T
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.

Travers answered 2/7, 2015 at 14:46 Comment(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.Spindle
E
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

Exposure answered 24/6, 2015 at 22:12 Comment(3)
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 github.com/symfony/symfony/blob/2.8/src/Symfony/Component/… and github.com/symfony/symfony/blob/2.8/src/Symfony/Component/…Spindle
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.Keshiakesia
"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?Spindle
A
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)

Ana answered 24/6, 2015 at 6:4 Comment(1)
What exactly are you suggesting? To reimplement all the symfony2 constraints? What is the point of using a symfony validator at all then?Spindle

© 2022 - 2024 — McMap. All rights reserved.