2010-07-22 8 views
3

sur le projet J'écris avec Symfony, il y aura très souvent des fieldsets dans les formulaires, donc je voudrais créer un mécanisme pour pouvoir regrouper les champs par fieldsets et toujours utiliser la méthode __toString() de mes formes. Sur this page, j'ai lu sur le sfWidgetFormSchema, et comment il pourrait être considéré comme un widget, ce qui permet d'imbriquer des champs. Voici donc ce que je faisais: j'ai créé les champs imbriqués:Implémentation de Fieldset dans les formulaires Symfony

$this->setWidgets(array(
     'customer' => new sfWidgetFormSchema(array(
     'customer_name'  => new sfWidgetFormInputText(), 
     'customer_email'  => new sfWidgetFormInputText(array()) 
    )), 
     'library'  => new sfWidgetFormSchema(array(
     'library_name'  => new sfWidgetFormInputText(), 
     'library_address' => new sfWidgetFormInputText(), 
     'library_city'  => new sfWidgetFormInputText(), 
     'library_postcode' => new sfWidgetFormInputText(), 
     'library_website' => new sfWidgetFormInputText() 
    )), 
     'message'  => new sfWidgetFormTextarea(array(),array("cols" => 50, "rows" => 10)), 
    )); 

Puis j'ai créé une classe fieldsetFormSchemaFormatter, qui enveloppe essentiellement des champs dans les balises, et associé avec les champs sfWidgetFormSchema:

foreach (array('customer', 'library') as $fieldset) 
    { 
     $this->widgetSchema[$fieldset]->addFormFormatter('tableless', 
     new tableLessFormSchemaFormatter($this->widgetSchema['customer'])); 
     $this->widgetSchema[$fieldset]->setFormFormatterName('tableless'); 
     $this->widgetSchema[$fieldset]->setNameFormat('%s'); 
    } 
    $this->widgetSchema->addFormFormatter('fieldset', 
     new FieldsetFormSchemaFormatter($this->widgetSchema, 
     'TableLessFormSchemaFormatter')); 
    $this->widgetSchema->setFormFormatterName('fieldset'); 

Et ça a juste bien fonctionné, j'ai eu ma forme de fieldset. Le problème que j'ai est avec la validation, qui n'est pas du tout décrite sur la page que j'ai liée plus tôt dans cette question. Les messages d'erreur apparaissent en haut du formulaire pour tous les champs sauf le champ "message" qui contient un message d'erreur juste après. Je ne pense pas que je serai capable d'afficher les messages d'erreur juste après les lignes et j'utiliserai toujours la construction echo $ form sans coder quelque chose de moche, donc je pense que je vais aller avec une autre implémentation. Je pense que les widgets sfWidgetFormSchema sont destinés à construire des champs interdépendants, qui auraient des règles de validation globales.

Comment implémenteriez-vous cette fonctionnalité fieldset?

+0

Pouvez-vous utiliser simplement des modèles verbeux au lieu et ajouter les fieldsets manuellement? Pas super élégant mais ça marcherait, ça dépend du nombre de formes que vous avez. – Luke

+0

@Luke: Oui, je pourrais, mais j'aimerais ne pas. J'implémente actuellement la fonctionnalité fieldset dans une classe héritant de sfWidgetFormSchema (je vais remplacer la méthode render()). Comme j'ai plusieurs formes dans de nombreux projets, je pense que ça en vaut la peine. – greg0ire

Répondre

3

Voici ce que j'ai trouvé après quelques recherches, cela semble fonctionner, mais le mécanisme des positions n'est plus utilisé lors du rendu. Je me demande si cela peut être problématique.

<?php 
class UcWidgetFormSchema extends sfWidgetFormSchema 
{ 
    /** 
    * An associative array with all the fieldsets 
    * <code> 
    * array(
    * "fieldset1" => array("fieldName1", "fieldName2"), 
    * "fieldset2" => array("fieldName3", "fieldName4"), 
    * ) 
    * </code> 
    * 
    * @var array 
    */ 
    private $fieldsets; 

    /** 
    * A fieldset-compatible constructor. 
    * 
    * @param mixed $fields  Initial fields. Values can be given this way: 
    * <code> 
    * array(
    * "fieldset1" => array(
    *  "field1" => $widget1, 
    *  "field2" => $widget2 
    * ) 
    * "fieldset1" => array(
    *  "field3" => $widget3, 
    *  "field4" => $widget4, 
    *  "field5" => $widget5 
    * ) 
    * "message" => $widget6 
    * ) 
    * </code> 
    * @param array $options An array of options 
    * @param array $attributes An array of default HTML attributes 
    * @param array $labels  An array of HTML labels 
    * @param array $helps  An array of help texts 
    */ 
    public function __construct($fields = null, $options = array(), 
    $attributes = array(), $labels = array(), $helps = array()) 
    { 
    $this->addOption('name_format', '%s'); 
    $this->addOption('form_formatter', null); 

    parent::__construct($options, $attributes); 

    if (is_array($fields)) 
    { 
     $fieldsets = array(); 
     foreach ($fields as $name => $value) 
     { 
     if (is_array($value)) 
     { 
      $fieldsets[$name] = array_keys($value); 
      foreach ($value as $valueName=> $valueWidget) 
      { 
      $this[$valueName] = $valueWidget; 
      } 
     } 
     else 
     { 
      $this[$name] = $value; 
     } 
     } 
     $this->setFieldsets($fieldsets); 
    } 
    else if (null !== $fields) 
    { 
     throw new InvalidArgumentException('sfWidgetFormSchema constructor takes an array of sfWidget objects.'); 
    } 

    $this->setLabels($labels); 
    $this->helps = $helps; 
    } 

    /** 
    * Setter for the fieldsets 
    * 
    * @param array $fieldsets an associative array 
    * 
    * @return null 
    */ 
    public function setFieldsets(array $fieldsets) 
    { 
    $fieldNames = array(); 
    foreach ($fieldsets as $fieldset => $fieldsetFieldNames) 
    { 
     $fieldNames = array_merge($fieldNames, $fieldsetFieldNames); 
    } 
    $availableFieldsNames = array_keys($this->getFields()); 
    if ($diff = array_diff(array_unique($fieldNames), $fieldNames)) 
    { 
     throw new InvalidArgumentException(
     'A field can only be used once in all fieldset. These do not: ' . 
     implode(', ', $diff)); 
    } 

    if ($diff = array_diff($fieldNames, $availableFieldsNames)) 
    { 
     throw new InvalidArgumentException(
     'Widget schema does not include the following field(s): ' . 
     implode(', ', $diff)); 
    } 
    $this->fieldsets = $fieldsets; 
    } 

    public function render($name, $values = array(), $attributes = array(), $errors = array()) 
    { 
    if(!$this->getFormFormatter() instanceof FieldsettedFormFormatterInterface) 
    { 
     throw new LogicException('The formatter you are using must implement FieldsettedFormFormatterInterface'); 
    } 

    if (null === $values) 
    { 
     $values = array(); 
    } 

    if (!is_array($values) && !$values instanceof ArrayAccess) 
    { 
     throw new InvalidArgumentException('You must pass an array of values to render a widget schema'); 
    } 

    $formFormat = $this->getFormFormatter(); 


    $groups  = array(); 
    $hiddenRows = array(); 
    $errorRows = array(); 
    $lonelyFields = $this->getPositions(); 
    $lonelyRows = array(); 

    // render each field 
    foreach ($this->fieldsets as $fieldset => $fieldNames) 
    { 
     $rows = array(); 
     foreach ($fieldNames as $name) 
     { 
     $lonelyFields  = array_diff($lonelyFields, array($name)); 
     $widget   = $this[$name]; 
     $value   = isset($values[$name]) ? $values[$name] : null; 
     $error   = isset($errors[$name]) ? $errors[$name] : array(); 
     $widgetAttributes = isset($attributes[$name]) ? $attributes[$name] : array(); 

     if ($widget instanceof sfWidgetForm && $widget->isHidden()) 
     { 
      $hiddenRows[] = $this->renderField($name, $value, $widgetAttributes); 
     } 
     else 
     { 
      $field = $this->renderField($name, $value, $widgetAttributes, $error); 

      // don't add a label tag and errors if we embed a form schema 
      $label = $widget instanceof sfWidgetFormSchema ? 
      $this->getFormFormatter()->generateLabelName($name) : 
      $this->getFormFormatter()->generateLabel($name); 
      $error = $widget instanceof sfWidgetFormSchema ? array() : $error; 

      $rows[] = $formFormat->formatRow($label, $field, $error, 
      $this->getHelp($name)); 
     } 
     $groups[$fieldset] = $rows; 
     } 
    } 

    foreach ($lonelyFields as $name) 
    { 
     $widget   = $this[$name]; 
     $value   = isset($values[$name]) ? $values[$name] : null; 
     $error   = isset($errors[$name]) ? $errors[$name] : array(); 
     $widgetAttributes = isset($attributes[$name]) ? $attributes[$name] : array(); 

     if ($widget instanceof sfWidgetForm && $widget->isHidden()) 
     { 
     $hiddenRows[] = $this->renderField($name, $value, $widgetAttributes); 
     } 
     else 
     { 
     $field = $this->renderField($name, $value, $widgetAttributes, $error); 

     // don't add a label tag and errors if we embed a form schema 
     $label = $widget instanceof sfWidgetFormSchema ? 
      $this->getFormFormatter()->generateLabelName($name) : 
      $this->getFormFormatter()->generateLabel($name); 
     $error = $widget instanceof sfWidgetFormSchema ? array() : $error; 

     $lonelyRows[] = strtr($formFormat 
      ->formatRow($label, $field, $error, $this->getHelp($name)), 
      array('%hidden_fields%' => '')); 
     } 
    } 

    $html = ''; 

    if ($groups) 
    { 
     // insert hidden fields in the last row 
     $i  = 0; 
     $maxGroup = count($groups); 
     foreach ($groups as $fieldset => $group) 
     { 

     for ($j = 0, $max = count($group); $j < $max; $j++) 
     { 
      $group[$j] = strtr($group[$j], array('%hidden_fields%' => 
      (($i == $maxGroup -1) && $j == $max - 1) ? 
       implode("\n", $hiddenRows) : '')); 
     } 

     $html .= $this->getFormFormatter() 
      ->formatFieldSet($fieldset, implode('', $group)); 
     $i++; 
     } 
    } 
    else 
    { 
     // only hidden fields 
     $lonelyRows[] = implode("\n", $hiddenRows); 
    } 
    $html .= implode('', $lonelyRows); 

    return $this->getFormFormatter() 
     ->formatErrorRow($this->getGlobalErrors($errors)) . $html; 
    } 
} 

Voici l'interface de votre formatter doit mettre en œuvre si vous voulez l'utiliser:

interface FieldsettedFormFormatterInterface 
{ 
    /** 
    * This method will be used to render a fieldset 
    * 
    * @param string $name the name of the widget 
    * @param string $widgets the widgets html 
    * 
    * @return string the html for the fieldset 
    */ 
    public function formatFieldset($name, $widgets); 
} 
Questions connexes