Skip to content

[Validator ]AbstractComparisonValidator propertyPath and NULL  #29831

Closed
@sgehrig

Description

@sgehrig

Symfony version(s) affected: 4.2.2

Description
Not sure whether this is a bug or the intended behaviour - but at least it seems to be inconsistent with my understanding on how null values are handled in the validator component.

Given two ?\DateTimeImmutable properties (nullable) in a class to define a timespan where NULL reflects unbounded, I'd like to use two comparison constraints to ensure that date 1 is always less than or equal to date 2. The AbstractComparisonValidator however only deals with NULL on the owning side of the constraint and does not handle NULL on the propertyPath constraint leading to validation errors with the LessThanOrEqual because the date 1 value (a \DateTimeImmutable) is not less than or equal to NULL. The GreaterThanOrEqual constraint is no problem - by coincidence - because a \DateTimeImmutable in date 2 is always greater than NULL in date 1.

How to reproduce

use Symfony\Component\Validator\Constraints as Assert;

class UpdateValidity
{
    /** 
     * @Assert\LessThanOrEqual(propertyPath="validUntil")
     *
     * @var \DateTimeImmutable|null
     */
    public $validFrom;

    /**
     * @Assert\GreaterThanOrEqual(propertyPath="validFrom")
     *
     * @var \DateTimeImmutable|null
     */
    public $validUntil;
}

$u = new UpdateValidity();
$u->validFrom = new \DateTimeImmutable('2019-01-01');
$u->validUntil = new \DateTimeImmutable('2019-12-31');
$validator->validate($u); // OK - validation succeeds

$u->validFrom = new \DateTimeImmutable('2020-01-01');
$validator->validate($u); // OK - validation fails

$u->validFrom = null;
$validator->validate($u); // OK - validation succeeds (by coincidence)

$u->validFrom = new \DateTimeImmutable('2019-01-01');
$u->validUntil = null;
$validator->validate($u); // Not OK - validation fails but should succeed in my opinion

Possible Solution

// vendor/symfony/validator/Constraints/AbstractComparisonValidator.php:50:62
if ($path = $constraint->propertyPath) {
    if (null === $object = $this->context->getObject()) {
        return;
    }

    try {
        $comparedValue = $this->getPropertyAccessor()->getValue($object, $path);
    } catch (NoSuchPropertyException $e) {
        throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: %s', $path, \get_class($constraint), $e->getMessage()), 0, $e);
    }
    // <new>
    if (null === $comparedValue) {
        return;
    }
    // </new>
} else {
    $comparedValue = $constraint->value;
}

Additional context
If required one could add a NotBlank or NotNull to the property to ensure that it must not be NULL.

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