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.