Skip to content

Validation skips violations in case with nested AtLeastOneOf constraint #54577

Closed
@GeoDpto

Description

@GeoDpto

Symfony version(s) affected

6.4

Description

When I make nested constraints AtLeastOneOf, the validator will skip all violations.

The Symfony validator employs two factories for initializing an instance of a constraint validator:

  1. Symfony\Component\ValidatorConstraintValidatorFactory - creates a new instance of the validator and caches it as an array.
  2. Symfony\Component\ContainerConstraintValidatorFactory - retrieves the validator from the DI container. As each unique service in a container is passed by reference, each validator is initialized only once.

The issue arises when creating a nested constraint, such as AtLeastOneOf. At line 752 of RecursiveContextualValidator, a new context for this validator is initialized, which is the root cause of the bug.

How to reproduce

To reproduce this bug, you can use the following code:

<?php

declare(strict_types=1);

use Symfony\Component\Validator\Constraints\AtLeastOneOf;
use Symfony\Component\Validator\Constraints\Choice;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\Type;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Validation;

require_once dirname(__DIR__) . '/vendor/autoload.php';

$values = [
    'AtLeastOneOf' => [
        'field1' => 'test',
        'AtLeastOneOfNested1' => [
            'AtLeastOneOfNested2' => \uniqid(),
            'field2' => \uniqid(),
        ],
    ],
    'test2' => '1',
];

$constraints = new Collection([
    'AtLeastOneOf' => new AtLeastOneOf([
        new Collection([
            'field1' => new NotBlank(),
            'AtLeastOneOfNested1' => new AtLeastOneOf([
                new Collection([
                    'AtLeastOneOfNested2' => new AtLeastOneOf([
                        new Type('int'),
                        new Choice(['test1', 'test2'])
                    ]),
                ]),
                new Collection([
                    'field2' => new Type('int'),
                ]),
            ]),
        ]),
        new Collection([
            'field3' => new NotBlank(),
            'AtLeastOneOfNested3' => new AtLeastOneOf([
                new Collection([
                    'field1' => new Type('int'),
                ]),
                new Collection([
                    'field2' => new Type('int'),
                ]),
            ]),
        ]),
    ]),
    'test2' => new NotBlank(),
]);

$violations = Validation::createValidator()->validate($values, $constraints);

echo $violations->count() . \PHP_EOL;

As we can see, incorrect data exists for the key AtLeastOneOfNested1, but the validator skips these violations.

Possible Solution

A possible solution is to consistently initialize a new instance of AtLeastOneOfValidator within a factory.

image
image

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions