web-dev-qa-db-fra.com

Comment valider une propriété dépendant d'une autre propriété dans Symfony 2

Est-il possible de valider une propriété d'une classe de modèle dépendant d'une autre propriété de la même classe?

Par exemple, j'ai cette classe:

class Conference
{
    /** $startDate datetime */
    protected $startDate;

    /** $endDate datetime */
    protected $endDate;
}

et je veux que Symfony 2.0 valide, que $startDate doit être après $endDate.

Est-ce possible par des annotations ou dois-je le faire manuellement?

28
Johannes Klauß

Oui avec le validateur de rappel: http://symfony.com/doc/current/reference/constraints/Callback.html

Sur symfony 2.0:

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContext;

/**
 * @Assert\Callback(methods={"isDateValid"})
 */
class Conference
{

    // Properties, getter, setter ...

    public function isDateValid(ExecutionContext $context)
    {
        if ($this->startDate->getTimestamp() > $this->endDate->getTimestamp()) {
                $propertyPath = $context->getPropertyPath() . '.startDate';
                $context->setPropertyPath($propertyPath);
                $context->addViolation('The starting date must be anterior than the ending date !', array(), null);
        }
    }
}

Sur la version principale de symfony:

    public function isDateValid(ExecutionContext $context)
    {
        if ($this->startDate->getTimestamp() > $this->endDate->getTimestamp()) {
            $context->addViolationAtSubPath('startDate', 'The starting date must be anterior than the ending date !', array(), null);
        }
    }

Ici, j'ai choisi d'afficher le message d'erreur dans le champ startDate.

20
Sybio

À partir de Symfony 2.4, vous pouvez également utiliser Expression contrainte de validation pour répondre à vos besoins. Je crois que c'est le moyen le plus simple de le faire. C'est plus pratique que la contrainte de rappel à coup sûr.

Voici un exemple de mise à jour de votre classe de modèle avec des annotations de contraintes de validation:

use Symfony\Component\Validator\Constraints as Assert;


class Conference
{
    /**
     * @var \DateTime
     *
     * @Assert\Expression(
     *     "this.startDate <= this.endDate",
     *     message="Start date should be less or equal to end date!"
     * )
     */
    protected $startDate;

    /**
     * @var \DateTime
     *
     * @Assert\Expression(
     *     "this.endDate >= this.startDate",
     *     message="End date should be greater or equal to start date!"
     * )
     */
    protected $endDate;
}

N'oubliez pas de activer les annotations dans la configuration de votre projet.

Vous pouvez toujours faire des validations encore plus complexes en utilisant syntaxe d'expression .

36
Slava Fomin II

Un autre moyen (au moins à partir de Symfony 2.3) consiste à utiliser le simple @Assert\IsTrue:

class Conference
{
    //...

    /**
     * @Assert\IsTrue(message = "Startime should be lesser than EndTime")
     */
    public function isStartBeforeEnd()
    {
        return $this->getStartDate() <= $this->getEndDate;
    }

    //...
}

Comme référence, documentation .

10
Damaged Organic

C'est encore plus simple depuis version 2.4 . Tout ce que vous avez à faire est d’ajouter cette méthode à votre classe:

use Symfony\Component\Validator\Context\ExecutionContextInterface;

/**
 * @Assert\Callback
 */
public function isStartBeforeEnd(ExecutionContextInterface $context)
{
    if ($this->getStartDate() <= $this->getEndDate()) {
        $context->buildViolation('The start date must be prior to the end date.')
                ->atPath('startDate')
                ->addViolation();
    }
}

La méthode buildViolation renvoie un générateur doté de deux autres méthodes pour vous aider à configurer la contrainte (comme les paramètres et la traduction).

9
bostaf

Une solution meilleure et plus propre https://symfony.com/doc/3.4/validation/custom_constraint.html Consiste à écrire 

  • une contrainte personnalisée (qui est essentiellement le message d'erreur)
  • et son validateur (qui est comme une fonction de contrôleur qui effectue le contrôle

Pour vérifier que l'entité va bien, ajoutez-le à la contrainte personnalisée (pas au validateur). 

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

Ce qui vous permet d'utiliser une instance de cette entité au lieu d'une valeur de propriété. Cela permet d'écrire dans le validateur:

public function validate($object, Constraint $constraint)
{
    #Your logic, for example:
    if($value1 = $object->getValue1())
    {
        if($value2 = $object->getValue2())
        {
            if($value1 === $value2)
            {
                # validation passed
                return True;
            }
            else
            {
                # validation failed
                $this->context->buildViolation($constraint->message)
                    ->setParameter('{{ string }}', $value1.' !== '.$value2)
                    ->addViolation();
            }

La meilleure partie est ce que vous devez écrire dans la classe d'entité:

use YourBundle\Validator\Constraints as YourAssert;

/**
 * Yourentity
 *
 * @ORM\Table(name="yourentity")
 * @ORM\Entity(repositoryClass="YourBundle\Repository\YourentityRepository")
 *
 * @YourAssert\YourConstraintClassName # <-- as simple as this

J'espère que cela pourra aider

0
nicolallias

Pour les validations de date, nous pouvons simplement utiliser les contraintes de comparaison GreaterThan et GreaterThanOrEqual.

class Conference
{
     /**
     * @var \DateTime
     * @Assert\GreaterThanOrEqual("today")
     */
    protected $startDate;

     /**
     * @var \DateTime
     * @Assert\GreaterThan("startDate")
     */
    protected $endDate;
}

Pour plus d'informations, voir contraintes de validation

0
Kisz Na