11

I have a form with choice field of entities from database:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('categories', 'document', array(
        'class' => 'Acme\DemoBundle\Document\Category',
        'property' => 'name',
        'multiple' => true,
        'expanded' => true,
        'empty_value' => false
    ));
}

This form will produce the list of checkboxes and will be rendered as:

[ ] Category 1
[ ] Category 2
[ ] Category 3

I want to disable some of the items by value in this list but I don't know where I should intercept choice field items to do that.

Does anybody know an solution?

dee-see
  • 23,668
  • 5
  • 58
  • 91
Vladimir Kartaviy
  • 666
  • 1
  • 7
  • 24

2 Answers2

13

you can use the 'choice_attr' in $form->add() and pass a function that will decide wether to add a disabled attribute or not depending on the value, key or index of the choice.

...
    'choice_attr' => function($key, $val, $index) {
        $disabled = false;

        // set disabled to true based on the value, key or index of the choice...

        return $disabled ? ['disabled' => 'disabled'] : [];
    },
...
Jacer Omri
  • 1,723
  • 22
  • 37
  • 1
    What's wrong with this solution? IMHO it's the right way to do it, see: http://symfony.com/doc/current/reference/forms/types/choice.html#choice-attr – David Kmenta Nov 01 '16 at 16:40
  • @DavidKmenta doesn't work with versions of Symfony prior to 2.7 – nomistic Mar 08 '17 at 15:38
  • 2
    Have in mind that some users can remove the disabled attribute using the browser inspector ! So you have to add more instructions ... – Sami Nov 12 '19 at 15:12
12

Just handled it with finishView and PRE_BIND event listener.

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('categories', 'document', array(
        'class' => 'Acme\DemoBundle\Document\Category',
        'property' => 'name',
        'multiple' => true,
        'expanded' => true,
        'empty_value' => false
    ));

    $builder->addEventListener(FormEvents::PRE_BIND, function (FormEvent $event) {
        if (!$ids = $this->getNonEmptyCategoryIds()) {
            return;
        }

        $data = $event->getData();

        if (!isset($data['categories'])) {
            $data['categories'] = $ids;
        } else {
            $data['categories'] = array_unique(array_merge($data['categories'], $ids));
        }

        $event->setData($data);
    });
}

...

public function finishView(FormView $view, FormInterface $form, array $options)
{
    if (!$ids = $this->getNonEmptyCategoryIds()) {
        return;
    }

    foreach ($view->children['categories']->children as $category) {
        if (in_array($category->vars['value'], $ids, true)) {
            $category->vars['attr']['disabled'] = 'disabled';
            $category->vars['checked'] = true;
        }
    }
}
Vladimir Kartaviy
  • 666
  • 1
  • 7
  • 24
  • This helped me get out of a sticky situation. Thank you. – Bogdan Feb 27 '13 at 07:46
  • Something change in sf 2.3.* ? Beacuse this solution dosen't work for me. – Pawel Jul 07 '13 at 07:08
  • 3
    This is insecure, as (if the html is manipulated) the choice is still possible. – Nemo64 Jul 07 '14 at 09:58
  • This post contains two different ways to disable checkboxes. I've spent hour before I got this. For me second way with finishView method is much easier to use. – hywak Jul 21 '15 at 13:00