Symfony Validation Trick: Dependent Validation

Vlad Gregurco
2 min readDec 24, 2018

--

It’s quite common case to have in projects dependent fields and we should somehow to validate them. Classic example is validation between start and end date, for example, booking.

Let’s suppose we have a Booking entity:

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
*
@ORM\Entity()
*/
class Booking
{
/**
*
@ORM\Column(type="integer")
*
@ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;

/**
*
@ORM\Column(type="datetime")
*/
private $startDate;

/**
*
@ORM\Column(type="datetime")
*/
private $endDate;

public function getId()
{
return $this->id;
}

public function getStartDate()
{
return $this->startDate;
}

public function setStartDate(\DateTime $startDate)
{
$this->startDate = $startDate;
}

public function getEndDate()
{
return $this->endDate;
}

public function setEndDate(\DateTime $endDate)
{
$this->endDate = $endDate;
}
}

It’s very small one. Just fields we need for this type of validation.

Next step is to create a Form Type class for this entity:

namespace App\Form;

use App\Entity\Booking;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Date;

class BookingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('endDate', DateType::class, [
'constraints' => [
new NotBlank(),
new Date(),
]
])
->add('startDate', DateType::class, [
'constraints' => [
new NotBlank(),
new Date(),
]
]);
}

public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Booking::class,
]);
}
}

As you can notice, we have generic validation rules: both fields are required and should have valid date value. But how to restrict the user not to mix the dates? It’s should not be acceptable to finish the booking before to start it, it’s obvious!

In this case Symfony provides us GreaterThan constraint. The trick happens here: we have to use this constraint not with static value, but with another field:

namespace App\Form;

use App\Entity\Booking;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\GreaterThan;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Date;

class BookingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('endDate', DateType::class, [
'constraints' => [
new NotBlank(),
new Date(),
]
])
->add('startDate', DateType::class, [
'constraints' => [
new NotBlank(),
new Date(),
new GreaterThan([
'propertyPath' => 'parent.all[endDate].data'
]),
]
]);
}

public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Booking::class,
]);
}
}

Render the form and try to enter wrong value:

This trick is applicable only for Form Types and in case you are writing validation rules directly in entities, then follow example from official documentation.

Read more about Forms in special article in Jobeet Tutorial.

--

--