Le blog de Noël GUILBERT

Aller au contenu Aller au menu

dimanche, mars 22 2009

symfony et sfForms : ajouter une étoile pour les champs obligatoires

Il est très courant sur le web d'avoir les champs obligatoires d'un formulaire marqués d'une étoile. Pourtant, avec symfony et les sfForm, ajouter ce caractère n'est pas si évident qu'il en à l'air.

La difficulté s'explique par le découplage total entre les principaux éléments d'un formulaire, à savoir les widgets et les validateurs. Ainsi, un widget n'as pas connaissance des validateurs qui lui sont attachés, et vice-versa. Pourtant, il est indispensable de savoir si un validateur à l'option "required". Pour pouvoir ajouter cette fameuse étoile, il va donc falloir trouver une solution pour que les deux éléments puissent interagir.

La semaine dernière, je suis tombé sur cet article, qui proposait une solution pour répondre à ce problème. Cette dernière me gène un peu car elle redéfinie directement le label en lui ajoutant des informations. Or, l'idéal serait d'ajouter ce caractère uniquement au moment du rendu : en effet, ce n'est que de l'affichage, donc du coté Vue du MVC.

Avec les sfForm, la Vue est caractérisée par la classe sfWidgetFormSchemaFormatter. Or cette classe a uniquement connaissance des widgets, il va donc falloir détourner légèrement son fonctionnement pour lui permettre d'avoir accès aux validateurs. Enfin, il va falloir surcharger la méthode generateLabelName(), dont le fonctionnement parle de lui même.

Sans plus attendre, voici donc notre nouveau formatter :

<?php

class sfWidgetFormSchemaFormatterRequiredFields extends sfWidgetFormSchemaFormatter
{
  protected
    validatorSchema ;

  /**
    * This constructor allow us to pass a sfValidatorSchema object to our formatter
    *
    */

  public function __construct(sfWidgetFormSchema $widgetSchema, sfValidatorSchema $validatorSchema)
  {
    parent::__construct($widgetSchema);

    $this->validatorSchema = $validatorSchema;
  }

  public function generateLabelName($name)
  {
    $label = parent::generateLabelName($name);
    // Is the field required ? If so we add a star to the label name
    $label .= $this->validatorSchema[$name]->getOption('required') ? ' <span class="required">*</span>' : '';

    return $label;
  }
}

Ensuite, il suffit de d'utiliser ce formatter à la place de celui par défaut. Pour cela, il suffit d'ajouter ces deux lignes dans la méthode configure() de votre formulaire:

$this->widgetSchema->addFormFormatter('RequiredFields', new sfWidgetFormSchemaFormatterRequiredFields($this->widgetSchema, $this->validatorSchema));
$this->widgetSchema->setFormFormatterName('RequiredFields');

Vous pouvez aussi surcharger la méthode setup() de la class BaseForm(Propel|Doctrine) pour ajouter ce comportement à tous vos formulaires.

Conclusion

Le découplage important des différents composants des sfForm rends ce genre de tache à priori simple plutôt compliquée, et cette solution nécessite une assez bonne connaissance du fonctionnement interne de ce framework. Mais en contrepartie, ça nous permet de gérer des choses plus complexes assez facilement.

mardi, mars 17 2009

Un formulaire d'inscription à CampaignMonitor avec symfony

Le but de ce post est de réaliser en quelques étapes un formulaire permettant d'inscrire un utilisateur à une liste de diffusion.

Pour ce faire, nous allons utiliser symfony, et CampaignMonitor. Ce dernier est outil d'emailing qui offre une API assez complète, et un portage en PHP pour faciliter son utilisation.

Pour commencer, vous aurez besoin d'un projet symfony (1.1 ou 1.2), et d'un compte campaign monitor (l'inscription est gratuite), ainsi que de la classe CampaignMonitor disponible ici.

Si vous venez tout juste de créer votre compte chez campaign monitor, vous devez créer un client et une nouvelle liste de diffusion avant de continuer.

Le formulaire d'inscription

Dans lib/form, créez un fichier CampaignMonitorSubscriptionForm.class.php, et ajouter le code ci-dessous:

class CampaignMonitorSubscriptionForm extends sfForm
{
  protected
    $campaignMonitor = null;
 
  public function setup()
  {
     $this->setOption('api_key', '1234567890ABCDEF');
     $this->setOption('client_id', '1234566789');
     $this->setOption('list_id', '123456789');

     $this->setWidgets(array(
       'email' => new sfWidgetFormInput()
     ));

     $this->setValidators(array(
       'email' => new sfValidatorEmail()
     ));

     $this->widgetSchema->setNameFormat('newsletter[%s]');
  }

  public function getCampaignMonitorInstance()
  {
    if (is_null($this->campaignMonitor))
    {
      $this->campaignMonitor = new CampaignMonitor($this->getOption('api_key'), $this->getOption('client_id'),  null, $this->getOption('list_id'));
    }

    return $this->campaignMonitor;
  }

  public function save()
  {
    $cm = $this->getCampaignMonitorInstance();

    if (!$cm->subscriberAddAndResubscribe($values['email'], null, $this->getOption('list_id')))
    {
      throw new Exception('An error occurred during the newsletter subscription');
    }
  }
}

Implémentation du module newsletter

Tout d'abord, il nous faut un module newsletter:

$> php symfony generate:module frontend newsletter

Il nous faut ensuite créer une action pour permettre l'affichage du formulaire et l'inscription de l'utilisateur.

Maintenant, utilisons la méthode executeIndex() de notre contrôleur:

class newsletterActions extends sfActions
{
 /**
  * Executes index action
  *
  * @param sfRequest $request A request object
  */

  public function executeIndex(sfWebRequest $request)
  {
    $this->form = new CampaignMonitorSubscriptionForm();

    $this->processForm($request);
  }

  protected function processForm($request)
  {
    if ($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter('newsletter'));

      if ($this->form->isValid())
      {
        $this->form->save();
        $this->getUser()->setFlash('notice', 'Your registration has been accepted');

        $this->redirect('newsletter/index');
      }
    }
  }
}

Et enfin, ajoutons l'affichage du formulaire dans le template indexSuccess.php:

<?php if ($sf_user->hasFlash('notice')): ?>
  <h1><?php echo $sf_user->getFlash('notice') ?></h1>
<?php else: ?>
  <?php echo $form->renderFormTag(url_for('newsletter/index')) ?>
    <table>
      <?php echo $form ?>
      <tr><td><input type="submit" /></td></tr>
    </table>
  </form>
<?php endif ?>

En conclusion

On pourrait aller plus loin dans cet exemple en prenant en compte les champs personnalisable de CampaignMonitor. Cela est assez facile à implémenter, je vous laisse donc regarder du coté de l'API de CampaignMonitor. Néanmoins, grâce à symfony et aux sfForm, nous avons facilement réalisé un formulaire d'inscription à une liste de diffusion.