代码之家  ›  专栏  ›  技术社区  ›  greg0ire

Symfony表单中的字段集实现

  •  3
  • greg0ire  · 技术社区  · 14 年前

    在我用Symfony编写的项目中,表单中经常会有字段集,因此我想创建一种机制,这样我就可以按字段集对字段进行分组,并且仍然使用表单的\uuToString()方法。 this page ,我了解了sfWidgetFormSchema,以及如何将其视为一个小部件,它支持嵌套字段。 下面是我所做的:

       $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 )),
        ));
    

    然后我创建了一个fieldsetFormSchemaFormatter类,它基本上将字段包装在标记中,并将其与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');
    

    结果很好,我拿到了我的字段集表格。我遇到的问题是验证,这在我之前链接的页面上根本没有描述。所有字段的错误消息都显示在窗体的顶部,但“message”字段后面有一条错误消息。我不认为我能够让错误消息显示在行的后面,并且仍然使用echo$form构造而不编码一些丑陋的东西,所以我认为我将使用另一个实现。 我认为sfWidgetFormSchema小部件旨在构建相互依赖的字段,这些字段将具有全局验证规则。

    如何实现此字段集功能?

    1 回复  |  直到 14 年前
        1
  •  3
  •   greg0ire    13 年前

    这是我经过一些研究后得出的结论,它似乎工作得很好,但渲染时不再使用位置机制。我想知道这是否有问题。

    <?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;
      }
    }
    

    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);
    }