Symfony 2 - rearrange form fields

2020-07-10 00:14发布

In our Symfony2 project we have a very complex structure for forms with embedded forms.... Now we got the requirement to bring the output of the form in an specific order.

And here is the problem: We use the form_widget(form) and now we are looking for a solution in the object (e.g. via annotations) or the formbuilder to move a specific field to the end of the form. in symfony 1.4 it was the widget-movefield() function, i guess...

Thx...

8条回答
叛逆
2楼-- · 2020-07-10 00:44

As I understand, you want to use only form_widget(form) in final template.

Let assume we have two inherited models (ModelA, ModelB) and form types for them (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
}

If you render formB, you will get A,B,C order, but you want A,C,B. To accomplish that, create form template and add reference to app. config file:

#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 %}

Now, when you render formB, you will see desired order and keep code structured. It happens because every widget rendered only once, so if you render it before calling parent block, you will change their order.

查看更多
男人必须洒脱
3楼-- · 2020-07-10 00:55

I had the same problem, but solved it in a different way. Here is my solution, as an idea for others who are searching for this problem.

You could add all the form fields in an event listener, because here you have all the objects data to decide on the fields order. You could for example use a method from the data object to decide on the fields order:

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);
        }
    }
}
查看更多
我只想做你的唯一
4楼-- · 2020-07-10 00:55

You can do it right in your controller before you render the template:

$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 works as well, however usort creates a non-associative array which will break the form rendering later on.

查看更多
Fickle 薄情
5楼-- · 2020-07-10 00:56
{{ 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) }}
查看更多
Luminary・发光体
6楼-- · 2020-07-10 00:58

You can re-order the fields using this bundle: https://github.com/egeloen/IvoryOrderedFormBundle

This allows you to do things like this:

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

This was going to be in core, but was rejected and pulled out into a bundle.

查看更多
SAY GOODBYE
7楼-- · 2020-07-10 00:59

here is the solution I came up with.

I created a class that my types extend.

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

Then you can use it in your inherited classes.

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

This class extends the one above

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'));
    }
}
查看更多
登录 后发表回答