Symfony2 Validation Datetime 1 should be before Datetime 2

10,718

Solution 1

You could write a custom DateRangeValidator.

class DateRange extends Constraint {

    public $message = "daterange.violation.crossing";
    public $emptyStartDate = "daterange.violation.startDate";
    public $emptyEndDate = "daterange.violation.endDate";

    public $hasEndDate = true;

    public function getTargets() {
        return self::CLASS_CONSTRAINT;
    }

    public function validatedBy() {
        return 'daterange_validator';
    }
}

class DateRangeValidator extends ConstraintValidator
{

    public function isValid($entity, Constraint $constraint)
    {

        $hasEndDate = true;
        if ($constraint->hasEndDate !== null) {
            $hasEndDate = $constraint->hasEndDate;
        }

        if ($entity->getStartDate() !== null) {
            if ($hasEndDate) {
                if ($entity->getEndDate() !== null) {
                    if ($entity->getStartDate() > $entity->getEndDate()) {
                        $this->setMessage($constraint->message);
                        return false;
                    }
                    return true;
                } else {
                    $this->setMessage($constraint->emptyEndDate);
                    return false;
                }
            } else {
                if ($entity->getEndDate() !== null) {
                    if ($entity->getStartDate() > $entity->getEndDate()) {
                        $this->setMessage($constraint->message);
                        return false;
                    }
                }
                return true;
            }
        } else {
            $this->setMessage($constraint->emptyStartDate);
            return false;
        }
    }

register it as a service:

parameters:
    register.daterange.validator.class:     XXX\FormExtensionsBundle\Validator\Constraints\DateRangeValidator

services:
    daterange.validator:
        class: %register.daterange.validator.class%
        tags:
            - { name: validator.constraint_validator, alias: daterange_validator }

And use it in your Entity:

use XXX\FormExtensionsBundle\Validator\Constraints as FormAssert;

/**
 *
 * @FormAssert\DateRange()
 */
class Contact extends Entity
{
    private $startDate;

    private $endDate;
}

Eventhough it seems a bit much for a simple thing like that, but experience shows, that one needs a date range validator more often than just once.

Solution 2

You could add a validation getter to the entity - Symfony2 Validation Getters

In your validation

Acme\YourBundle\Entity\Employment:
    getters:
        datesValid:
            - "True": { message: "The start date must be before the end date" }

And then in your entity

public function isDatesValid()
{
    return ($this->startDate < $this->endDate);
}

Solution 3

There is Yet Another Solution: Validation by using Expression Language:

use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Column(type="date", nullable=true)
 * @Assert\Date()
 */
private $startDate;

/**
 * @ORM\Column(type="date", nullable=true)
 * @Assert\Date()
 * @Assert\Expression(
 *     "this.getStartDate() < this.getEndDate()",
 *     message="The end date must be after the start date"
 * )
 */
private $endDate;

You can add this constraint to $startDate if you want.

Share:
10,718

Related videos on Youtube

Simon
Author by

Simon

Updated on June 03, 2022

Comments

  • Simon
    Simon almost 2 years

    I'm looking through the Symfony2 Validation Reference but I'm not finding what I need.

    I have a class Employment with a StartDate and EndDate. I would like to add an \@Assert() where it verifies that StartDate is always BEFORE EndDate. Is there a standard way of comparing class attributes as a Validation Constraint or should I create a custom validation constraint?

    class Employment {
    
        /**
        * @ORM\Id
        * @ORM\Column(type="integer")
        * @ORM\GeneratedValue(strategy="AUTO")
        * @Expose() 
        */
        protected $id;
    
        /**
        * @ORM\Column(type="datetime") 
        * @Expose()
        * @Assert\DateTime()
        */
        protected $startDate;
    
        /**
        * @ORM\Column(type="datetime", nullable=TRUE)
        * @Expose()
        * @Assert\DateTime()
        */
        protected $endDate;
    
    ...
    }
    
  • Simon
    Simon about 11 years
    Thank you for reminding me of this approach and possibility. I've accepted the other reply as the answer because of reuseability.
  • qooplmao
    qooplmao about 11 years
    Yeah, I prefer the other one too.
  • qooplmao
    qooplmao over 9 years
    I know this is quite old but I keep getting notifications about it so this may be of some use to some people. There is now (there may have been before but I don't remember) a pre-built version of this constraint in the PUGXExtraValidatorBundle (github.com/PUGX/PUGXExtraValidatorBundle/blob/master/Resour‌​ces/…).
  • Benjamin Lucas
    Benjamin Lucas about 9 years
    New in dev and symfony, where $constraint->hasEndDate comes ? Is there an hasEndDate method in $constraint ? Or should I create it in my entity ? And where is the return message ? Thank you for this solution.
  • DerStoffel
    DerStoffel about 9 years
    @BenjaminLucas I added the missing fields in the code above. Thanks for the hint.
  • Scaramouche
    Scaramouche almost 9 years
    what should I use to use this class u propose?
  • Cassiano
    Cassiano almost 8 years
    less code, less maintenance. Thanks for the simple solution.
  • Adib Aroui
    Adib Aroui over 7 years
    @Simon, please to explain to me what do you mean by reusability here, thanks a lot in advance
  • Simon
    Simon over 7 years
    @whitelettersinblankpapers As opposed to the accepted answer this answer can only be used in this entity; If you want to use the same validator against other entities you'll have to write the same code again for your other entities. This is against the DRY principle. The accepted answer registers the validator as a service so it can be easily reused and shared between other entities.
  • Adib Aroui
    Adib Aroui over 7 years
    @Simon, thank you very much Sir, now I understand :)
  • Rubinum
    Rubinum almost 7 years
    this answer is way easier and perfect for this requirements.