Search code examples
phpsymfonysymfony-validator

How to validate if an element of an array is an array itself?


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


Solution

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