Symfony Form utilisation avancée des Validateurs

Dans le cadre d'un projet Symfony, je me suis heurté à une problématique d'apparence simple : l'utilisateur doit pouvoir changer ses informations personnelles, dont son mot de passe.

La difficulté est apparue au moment de mettre en place les validateurs de formulaire.

Le formulaire contient les champs suivants :
- nom
- prénom
- mot de passe actuel
- nouveau mot de passe
- confirmation de nouveau mot de passe

L’utilisateur doit pouvoir changer son nom ou son prénom sans forcément changer son mot de passe.
Il n'est pas non plus envisageable de pré-remplir le champ mot de passe actuel.
Pour pouvoir changer le mot de passe, il est nécessaire de connaitre l'ancien.

Il faut donc faire les contrôles suivants :
1. Si le champ nouveau mot de passe est rempli, alors le champ confirmation de nouveau mot de passe doit contenir la même chose
2. Si le champ nouveau mot de passe est rempli, alors le champ mot de passe actuel ne doit pas être vide. Autrement dit : les champs mot de passe actuel et nouveau mot de passe doivent être simultanément remplis ou bien simultanément vide.
3. Si le champ mot de passe actuel est rempli alors, il doit effectivement contenir le bon mot de passe

On peut résumer comme ceci :

 
(new_password == retype_new_password)
&&
(
    (
        (current_password != '') && (new_password != '') && (current_password == 'CORRECT_CURRENT_PASSWORD')
    )
    || (
        (current_password == '') && (new_password == '')
    )
)
 
 

Avec les validateurs en place, cela donne ceci :

$this->mergePostValidator(
            new sfValidatorAnd(
                array(
                    new sfValidatorSchemaCompare( 
                        'password',
                        sfValidatorSchemaCompare::EQUAL,
                        'retype_password', 
                        array(),
                        array('invalid' => 'Les deux nouveaux mots de passe doivent etre identique')
                    ),
                    new sfValidatorOr(
                        array(
                            new sfValidatorAnd(
                                array(
                                    new sfValidatorSchemaFilter('password', new sfValidatorString(array('required' => true),array('required'=>'Ce champ est obligatoire'))),
                                    new sfValidatorSchemaFilter('old_password', new sfValidatorString(array('required' => true),array('required'=>'Ce champ est obligatoire'))),
                                    new sfValidatorSchemaFilter('old_password', new sfValidatorCallback(array('callback' => array($this->getObject(),'checkPasswordValidator')))),
                                ),
                                array('halt_on_error' => true)
                            ),
                            new sfValidatorAnd(
                                array(
                                    new sfValidatorSchemaFilter('password', new sfValidatorRegex(array('pattern' => '/^$/','required' => false),array('invalid' => ' '))),
                                    new sfValidatorSchemaFilter('old_password', new sfValidatorRegex(array('pattern' => '/^$/','required' => false),array('invalid' => ' ')))
                                )
                            )
                        )
                    )
                ),
                array('halt_on_error' => true)
            )
        ); 

Les deux difficultés auxquelles je me suis retrouvé confronté sont les suivantes :
- contrôle de deux champs simultanément (tous les deux remplis ou tous les deux vides)
- vérifier que le mot de passe actuel rentré est le bon

Contrôler deux champs simultanéments

Pour résoudre le premier problème, je me suis basé sur les validateurs suivants.
Ce sont des 'supers-validateurs' dont le fonctionnement est un peu différents des validateurs habituels.

mergePostValidator

mergePostValidator ($validator)

Le validateur mergePostValidator permet d'ajouter un validateur après le passage de tous les autres validateurs. Les données sont ainsi déjà vérifiées et nettoyées.

sfValidatorAnd

sfValidatorAnd($validators, $options, $messages)

sfValidatorAnd permet d’agréger plusieurs validateurs avec un opérateur logique ET

sfValidatorOr

sfValidatorOr($validators, $options, $messages)

sfValidatorAnd permet d’agréger plusieurs validateurs avec un opérateur logique OU

sfValidatorSchemaCompare

sfValidatorSchemaCompare($leftField, $operator, $rightField, $options, $messages) 

sfValidatorSchemaCompare sert à comparer les valeurs de deux champ d'un formulaire

sfValidatorSchemaFilter

sfValidatorSchemaFilter($field, $validator, $options, $messages)

sfValidatorSchemaFilter permet d'ajouter à la volée un validateur pour un champ donné. C'est très pratique lors de l’utilisation d'un PostValidateur

Vérifier un champ avec une donnée externe au formulaire

Pour le second problème il me fallait déporter le contrôle du mot de passe

sfValidatorCallback

sfValidatorCallback($options, $messages) 

Ce validateur, plus classique, permet en effet de désigner un fonction à appeler qui se chargera de valider la donnée.
Je peux ainsi confier la responsabilité à une méthode de mon objet User de vérifier que le mot de passe saisie est le bon.

postheadericon Commentaires

  • Avencinlect Thursday 28 July à 13h05

    beaucoup appris

  • rim2004m Tuesday 18 March à 10h07

    bonjour,
    je suis encore débutante en symfony, je veux savoir comment tester si un champs dans un formulaire est bien rempli..

    s'il vous plait je veux une réponse claire..
    merci d'avance