Navigation

Related Articles

Back to Latest Articles

Snippet sfValidatorEmailList


Samuel Breton
Snippet sfValidatorEmailList

La version de Symfony 1.3 vient de passer en ALPHA2. Etant en cours de développement d’un projet sur cette version j’en profite pour tester les nouvelles fonctionnalités. Parmi celles-ci, il y a le sfMailer. Je ne vais pas m’étendre sur le sujet car on peut déjà trouver pas mal de posts sur internet concernant cette classe notamment dans les jobeet : Day 16 The Mailer .

Je me suis par contre amusé à coder un petit validator, qui il me semble, se couple bien avec le sfMailer : sfValidatorEmailList. Son nom est assez explicite mais pourquoi tester des listes de mails ? Tout simplement parceque sfMailer permet d’envoyer des mails groupés !

  /**
   * Creates a new message.
   *
   * @param string|array $from    The from address
   * @param string|array $to      The recipient(s)
   * @param string       $subject The subject
   * @param string       $body    The body
   *
   * @return Swift_Message A Swift_Message instance
   */

Dans les commentaires de la fonction compose() on peut voir que l’on a la possibilité de passer un string ou un tableau. sfValidatorEmailList va ainsi nous permettre de valider une liste de mails séparés par un caractère quelconque.

Tout d’abord je vais créer un formulaire classique qui me permettra de saisir les adresses emails, le sujet et le corps de mon email.

 
class lkInvitationForm extends sfForm
{
 
    public function setup()
    {
 
        if ( ! $this->getOption('mailer') instanceof sfMailer )
        {
            throw new RuntimeException('A valid mailer option is required to send an email from this form');
        }
 
        $this->setWidgets(array(
            'email'         => new sfWidgetFormInputText(array('label' => 'Adresse(s) email')),
            'title'         => new sfWidgetFormInputText(array('label' => 'titre du mail')),
            'body'          => new sfWidgetFormTextarea(array('label' => 'Corps du mail')),
            ));
 
        $this->setValidators(array(
            'email'   => new sfValidatorEmailList(array('required' => true), array('invalid' => '"%value%" invalide')),
            'title'    => new sfValidatorString(array('required' => true), array('invalid' => '"%value%" invalide')),
            'body'    => new sfValidatorString(array('required' => true), array('invalid' => '"%value%" invalide')),
            ));
 
        $this->widgetSchema->setNameFormat('invitation[%s]');
        $this->errorSchema = new sfValidatorErrorSchema($this->validatorSchema);
    }
 
    /**
     * Bind form and send mail
     *
     * @param sfWebRequest $request
     */
    public function bindAndSend(array $taintedValues = array())
    {
        $this->bind($taintedValues);
        if ( $this->isValid() )
        {
            $mailer = $this->options['mailer'];
            return $mailer->composeAndSend('contact@lexik.fr', $this->getValue('email'), $this->getValue('title'), $this->getValue('body'));
        }
 
    }
 
}

J’utilise donc pour mon email le sfValidatorEmailList dont voici le code :

 
class sfValidatorEmailList extends sfValidatorEmail
{
 
  /**
   * @see sfValidatorEmail
   */
    protected function configure($options = array(), $messages = array())
    {
        parent::configure($options, $messages);
 
        $this->addOption('separator', ',');
    }
 
  /**
   * @see sfValidatorString
   */
    public function doClean($value)
    {
        $retVal = array();
        $valueError = array();
        $mails = explode($this->getOption('separator'), $value);
 
        foreach ($mails as $mail)
        {
            try
            {
                $retVal[] = parent::doClean(trim($mail));
            }
            catch (sfValidatorError $e)
            {
                $valueError[] = $e->getValue();
            }
        }
 
        if ( ! empty ($valueError) )
        {
            throw new sfValidatorError($this, 'invalid', array('value' => implode(', ', $valueError)));
        }
 
        return $retVal;
    }
 
}

Quelques explications :

Au niveau de la configuration on peut indiquer quel sera le caractère de séparation. Ensuite, la fonction doClean() qui se charge de tester la validité d’une valeur va transformer le string que l’on récupère en tableau et va appeler le doClean de sfEmailValidator. Afin de permettre un rendu plus propre du message d’erreur, j’utilise un try-catch qui me permet de lister tous les emails invalides. Une fois la boucle terminée, je n’ai plus qu’a créer mon exception avec toutes les valeurs catchées.
Je peux par la suite passer le $this->getValue(’email’) à ma fonction composeAndSend() puisque le validator me retourne un tableau.

Exemple d’une erreur dans un seul email

simple_erreur

Exemple d’une erreur dans plusieurs emails

double_erreur

Maintenant lorsque j’affiche mon formulaire, les utilisateurs pourront saisir des adresses emails séparées d’une virgule pour envoyer à plusieurs contacts.

Show Comments (5)

Comments

  • NiKo

    C’est pas une super idée d’utiliser sfContext::getInstance() dans le formulaire ; il vaut mieux passer le mailer en option de ce dernier (et du même coup le mail de l’expéditeur tant qu’à faire), comme ça par exemple : http://pastie.org/654442

    Ainsi le formulaire devient facilement testable, grâce au découplage ainsi opéré.

    • Article Author
  • [MA]Pascal

    Pratique comme validateur, par contre comme NiKo je comprends pas trop l’implémentation.

    Pourquoi passer sfWebRequest $request à bindAndSend ? tu es déjà dans un form qui est normalement valide donc hydraté.

    Quitte à mettre des params à cette méthode c’est ici que j’aurai fait passer $this->getMailer();

    Pourquoi le mettre en param de tout le form ?

    • Article Author
  • yoye

    Je viens de corriger cela par contre au niveau de mon action je préfère instancier mon objet comme suit :

    $this->form = new lkInvitationForm(array(), array(‘mailer’ => $this->getMailer()));

    je ne sais pas si c’est une bonne ou mauvaise pratique mais je n’aime pas trop surcharger mon action.

    Après on peut aussi se poser la question à savoir si on peut rédéfinir le constructeur car le tableau vide en premier paramètre va correspondre à 90% de mes instanciations et le mailer étant obligatoire cela devient de la dependency injection.

    • Article Author
  • NiKo

    C’est pour ça que j’aime bien le setOption() qui se chaîne aisément grâce à son interface fluide. J’aime pas trop le passage d’arguments au constructeur, notamment du fait de la capacité de ce dernier à être surchargé en PHP.

    Les goûts et les couleurs…

    • Article Author
  • Richtermeister

    Very nice validator! Elegant and useful, thank you for sharing.

    I think you should try to submit this to be included in the symfony core in the future.

    Have a great day,
    Daniel

    • Article Author

Recevez nos articles