Search code examples
phpsymfonyeventssymfony-3.3

Add current object value to the ChoiceType values if missing


I am currently converting an application to Symfony 3.3 and I am scratching my head as to how I can achieve the following.

I have a ChoiceType field, called Responsible (String values), which is populated from a database view. I expect to see the Responsible field already filled when going in edit mode, which is doing so when the record Responsible value is part of the Responsible field values.

But the values have changed since then, so when I get to edit an existing record, it will appear as Please Select when the value is not part of the already populated values.

My goal is to add that missing value to the Responsible field values so that it is pre-selected, but I couldn't find how yet.

I tried to see if there was an option in the ChoiceType documentation, but it seems like I have to go for the onPreSetData event to do so, but even there, I can only find how to dynamically add fields, not values to existing fields.

As anybody found out how to do so, and which is the "proper" way to do so?

Thanks.

Edit: Thanks to @matval answer, there was only missing something to find if the current value is in the choices so we don't have duplicate values, such as if (!array_key_exists($entity->getResponsible(), $choices)).

/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        // Some preceding code...

        // onPreSetData
        ->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSet'))
}

/**
 * On pre set event.
 *
 * @param FormEvent $event
 * @throws \Exception
 */
public function onPreSet(FormEvent $event)
{
    // Get entity & form
    $entity = $event->getData();
    $form = $event->getForm();

    // Fill choices with responsibles from the users table
    $choices = $this->fillResponsibles();

    // If the key does not exists in the choices, add it.
    if (!array_key_exists($entity->getResponsible(), $choices)) {
        $choices[$entity->getResponsible()] = $entity->getResponsible();
    }

    $form->add('responsible', ChoiceType::class, [
        'choices' => $choices,
        'placeholder' => '-- Please Select --',
        'label' => 'Responsible',
        'required' => false,
    ]);
}

Solution

  • Form events are the proper way to do it. They are the best way to make dynamic forms. As you can see in the symfony doc you should add your Responsible field during the PRE_SET_DATA event.

    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $entity = $event->getData();
        $choices = ... // populate from db
        $choices[] = $entity->getResponsible();
        $form = $event->getForm();    
        $form->add('responsible', ChoiceType::class, [
            'choices' => $choices,
        ]);
    });
    

    If you want to keep dynamic field Responsible in your form type (maybe reuse for create action) you can still use the same event. All you need to do is remove the field and add it again.

    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $entity = $event->getData();
        $choices = ... // populate from db
        $choices[] = $entity->getResponsible();
        $form = $event->getForm(); 
        $form->add('responsible', ChoiceType::class, [
            'choices' => $choices,
        ]);
    });