Videa Blog

How to use Comparison Constraints with Symfony/Validator

Jáchym Toušek  

With Symfony/Validator there is no obvious way to implement validations like comparing a value to another property on the same object. There are several articles about this topic already but literally all of them are completely outdated. In this article I'll cover the correct way to solve this.

Update 2017-12-02: In Symfony 3.4+ comparison constraints have a propertyPath option. In this case you could use @Assert\GreaterThanOrEqual(propertyPath="startDate") instead of the Expression constraint.

Example use case

For date interval ensure that starting date is lower than ending date.

use Symfony\Component\Validator\Constraints as Assert;

class Event
{
    /**
     * @var \DateTime
     * @Assert\Type("DateTime")
     */
    protected $startDate;

    /**
     * @todo Add validator constraint that end date cannot be lower than start date.
     * @var \DateTime
     * @Assert\Type("DateTime")
     */
    protected $endDate;
}

The outdated solution

The obvious solution is to create a custom constraint and validator. It's explained in several articles and StackOverflow questions. Note that all of these are several years old. With the solution they recommend you might easily end up with a lot of custom constraints. Other answers suggest using the Callback constraint instead but that's still more complicated than necessary.

The correct solution

The point of this article is that Symfony actually does have an easy mechanism to do these validations. The Expression constraint is not something new either. It's just not covered in the documentation very well and less experienced developers can easily miss it.

The Expression constraint takes advantage of the ExpressionLanguage component. In the expression you can use the value and this placeholders to access the value itself and the underlying object respectively.

Example

use Symfony\Component\Validator\Constraints as Assert;

class Event
{
    /**
     * @var \DateTime
     * @Assert\Type("DateTime")
     */
    protected $startDate;

    /**
     * @var \DateTime
     * @Assert\Type("DateTime")
     * @Assert\Expression("value >= this.startDate")
     */
    protected $endDate;
}

Indeed it's this easy! More importantly the Expression constraint can help you solve many other situations. Plus the ExpressionLanguage can be extended with your own functions.

Usage with Nette Framework

If you want to use the Symfony/Validator component and the Expression constraint in your Nette application you will need these libraries: