【问题标题】:Symfony 2 - rearrange form fieldsSymfony 2 - 重新排列表单域
【发布时间】:2012-10-18 10:04:27
【问题描述】:

在我们的 Symfony2 项目中,我们有一个非常复杂的带有嵌入式表单的表单结构......现在我们需要按特定顺序输出表单。

这就是问题所在:我们使用 form_widget(form),现在我们正在寻找对象中的解决方案(例如通过注释)或 formbuilder 将特定字段移动到表单的末尾。在 symfony 1.4 中它是 widget-movefield() 函数,我猜...

谢谢...

【问题讨论】:

    标签: symfony symfony-forms symfony-2.1


    【解决方案1】:

    您可以使用此捆绑包重新排序字段:https://github.com/egeloen/IvoryOrderedFormBundle

    这允许你做这样的事情:

    $builder
    ->add('g', 'text', array('position' => 'last'))
    ->add('a', 'text', array('position' => 'first'))
    ->add('c', 'text')
    ->add('f', 'text')
    ->add('e', 'text', array('position' => array('before' => 'f')))
    ->add('d', 'text', array('position' => array('after' => 'c')))
    ->add('b', 'text', array('position' => 'first'));
    

    这将是核心,但被拒绝并被拉成一个包。

    【讨论】:

      【解决方案2】:

      今天在表单元素排序方面遇到了同样的问题。

      最终得到一个 trait,它将覆盖 FinishView 方法并重新排序 FormView 的 children 属性中的项目:

      trait OrderedTrait
      {
          abstract function getFieldsOrder();
      
          public function finishView(FormView $view, FormInterface $form, array $options)
          {
              /** @var FormView[] $fields */
              $fields = [];
              foreach ($this->getFieldsOrder() as $field) {
                  if ($view->offsetExists($field)) {
                      $fields[$field] = $view->offsetGet($field);
                      $view->offsetUnset($field);
                  }
              }
      
              $view->children = $fields + $view->children;
      
              parent::finishView($view, $form, $options);
          }
      }
      

      然后在 type 中实现 getFieldsOrder 方法:

      use OrderedTrait;
      
      function getFieldsOrder()
      {
          return [
              'first',
              'second',
              'next',
              'etc.',
          ];
      }
      

      【讨论】:

      • 这是一个很好、干净的解决方案!谢谢!
      • 很容易扩展这段代码,以便为getFieldsOrder返回的字段列表添加权重,如果有人需要,请随时与我联系。
      【解决方案3】:

      无需“重新排序”字段。您只需为每个字段分别调用form_label 和/或form_widget。假设您使用Twig,例如,您可以这样做:

      <div>{{ form_label(form.firstName) }}</div>
      <div>{{ form_widget(form.firstName) }}</div>
      
      <div>{{ form_label(form.lastName) }}</div>
      <div>{{ form_widget(form.lastName) }}</div>
      

      【讨论】:

      • 谢谢,但这是不可能的。我们的结构很复杂,需要form_widget(form)函数,所以我们必须在后端做这个!
      • 您是否尝试过按所需顺序向FormBuilder 实例添加字段?
      • 那不可能。我们有大约 80 种具有依赖、继承等的对象类型,所以我们不能手动操作这些东西......
      • 好的,我的同事做了黑客,很脏,但它有效:{% set label = form_widget(form.orderlabel) %} {% set input = form_widget(form.orderinput) %} { { form_rest(form) }} {{ label|raw }} {{ label|input }} 会很棒,如果 symfony 以后阻止我们做这样的事情:D 如果有人知道一个干净的解决方案,我们很高兴得到好的答案
      【解决方案4】:

      这是我想出的解决方案。

      我创建了一个我的类型扩展的类。

      namespace WF\CORE\CoreBundle\Form;
      
      use Symfony\Component\Form\AbstractType;
      use Symfony\Component\Form\FormBuilderInterface;
      use Symfony\Component\OptionsResolver\OptionsResolverInterface;
      
      class WfBaseForm extends AbstractType
      {
      
          protected function useFields(FormBuilderInterface $builder, $fields)
          {
              foreach ($builder->all() as $field) {
      
                  if (!in_array($field->getName(), $fields))
                      $builder->remove($field->getName());
              }
          }
      
      
          public function reorder(FormBuilderInterface $builder, $keys = array())
          {
              $fields         = $builder->all();
              $ordered_fields = array_merge(array_flip($keys), $fields);
              $this->useFields($builder, array());
      
              foreach($ordered_fields as $field)
                  $builder->add($field);
      
          }
      
          public function getName()
          {
              return 'base';
          }
      }
      

      然后你可以在你继承的类中使用它。

      class AddressType extends WfBaseForm
      { 
      public function buildForm(FormBuilderInterface $builder, array $options)
          {
              // I add as many fields as I need
              $builder->add( '…');
      }
      

      这个类扩展了上面的类

      class ModifyAddressType extends BaseType
      {
          public function buildForm(FormBuilderInterface $builder, array $options)
          {
              parent::buildForm($builder, $options);
      
              $builder->add('title', 'text', array('constraints' => array(new NotBlank())));
      
              $this->reorder($builder, array('title', 'firstname', 'lastname'));
          }
      }
      

      【讨论】:

        【解决方案5】:

        我遇到了同样的问题,但以不同的方式解决了它。这是我的解决方案,作为寻找此问题的其他人的想法。

        您可以在事件侦听器中添加所有表单字段,因为在这里您拥有所有对象数据来决定字段顺序。例如,您可以使用数据对象中的方法来决定字段顺序:

        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            // Don't use $builder->add(), or just for those fields which are always 
            // at the beginning of the form.
        
            $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
                $entry = $event->getData();
                $form = $event->getForm();
        
                // Now add all the fields in the order you need them to be, e.g by reading
                // the needed order from the data entry. 
                if ($entry->isFieldAFirst()) { 
                     $form->add(fieldA);
                     $form->add(fieldB);
                } else {
                    $form->add(fieldB);
                    $form->add(fieldA);
                }
            }
        }
        

        【讨论】:

          【解决方案6】:

          据我了解,您只想在最终模板中使用 form_widget(form)。

          假设我们有两个继承的模型(ModelA、ModelB)和它们的表单类型(ModelAType、ModelBType)

          class ModelA {
            private $A;
            private $B;
          
            // Getters and setters
          }
          
          class ModelB extends ModelA {
            private $C;
          
            // Getters and setters
          }
          
          /**
           * @DI\Service(id = "form.type.modelA")
           * @DI\Tag("form.type", attributes={ "alias":"model_a_type" })
           */
          class FormAType extends AbstractType {
            public function buildForm(FormBuilderInterface $builder, array $options)
            {
              $builder
                ->add('A')
                ->add('B')
              ;
            }
          
             // getName and so on
          }
          
          /**
           * @DI\Service(id = "form.type.modelA")
           * @DI\Tag("form.type", attributes={ "alias":"model_b_type" })
           */
          class FormAType extends AbstractType {
            public function getParent() {
              return "model_a_type";
            }
          
            public function buildForm(FormBuilderInterface $builder, array $options)
            {
              $builder
                ->add('C')
              ;
            }
          
             // getName and so on
          }
          

          如果你渲染formB,你会得到A,B,C的顺序,但是你想要A,C,B。 为此,请创建表单模板并添加对应用程序的引用。配置文件:

          #app/config/config.yml
          twig:
             ....
              form:
                  resources:
                      - YourAppBundle:Form:fields.html.twig
          
          
          {# src/YourAppBundle/Resources/views/Form/fields.html.twig #}
          {% block model_a_type_widget %}
             {{ form_widget(form.A) }}
             {{ form_widget(form.B) }}
          {% endblock model_a_type_widget %}
          
          {% block model_b_type_widget %}
             {{ form_widget(form.A) }}
             {{ form_widget(form.C) }}
          
             {{ block('model_a_type_widget') }}
          {% endblock model_b_type_widget %}
          

          现在,当您渲染 formB 时,您将看到所需的顺序并保持代码结构化。 这是因为每个小部件只渲染一次,所以如果你在调用父块之前渲染它,你会改变它们的顺序。

          【讨论】:

            【解决方案7】:

            您可以在渲染模板之前直接在控制器中执行此操作:

            $form = ...
            
            $view = $form->createView();
            
            // Perform standard array operations to reorder: `$view->children`
            // Here's a very simple example:
            
            $firstField = $view->children['firstField'];
            unset($view->children['firstField']);
            $view->children['firstField'] = $firstField;
            
            return array('form' => $view);
            

            uasort 也可以,但是usort 会创建一个非关联数组,这将在稍后破坏表单呈现。

            【讨论】:

              【解决方案8】:
              {{ form_start(form) }}
              
                  <div>{{ form_label(form.title) }}</div>
                  <div>{{ form_widget(form.title,{'id': 'blog_title'} )}}</div>
              
                  <div>{{ form_label(form.tag) }}</div>
                  <div>{{ form_widget(form.tag,{'id': 'blog_tag'} )}}</div>
              
                  <div>{{ form_label(form.category) }}</div>
                  <div>{{ form_widget(form.category,{'id': 'blog_category'} )}}</div>
              
                  <div>{{ form_label(form.image) }}</div>
                  <div>{{ form_widget(form.image,{'id': 'blog_image'} )}}</div>
              
                  <div>{{ form_label(form.body) }}</div>
                  <div>{{ form_widget(form.body,{'id': 'editor'} )}}</div>
              
                  <input type="submit" class="btn btn-success" value="Create" />
              {{ form_end(form) }}
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-09-18
                • 2012-11-15
                • 1970-01-01
                相关资源
                最近更新 更多