Symfony 2 - rearrange form fields

10,527

Solution 1

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.

Solution 2

Had same issue today with the form elements ordering.

Ended up with a trait that will override finishView method and reorder items in children property of a FormView:

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

Then in type implement getFieldsOrder method:

use OrderedTrait;

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

Solution 3

There's no need to "reorder" fields. All you need to do is to call form_label and/or form_widget for each field individually. Assuming you use Twig you could, for example, do:

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

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

Solution 4

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

Solution 5

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);
        }
    }
}
Share:
10,527
snirgel
Author by

snirgel

Updated on June 13, 2022

Comments

  • snirgel
    snirgel almost 2 years

    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...