Learn how to retrieve the value of a dynamic added field in your form in Symfony 3.

If you are working with forms in Symfony, you know how easy is to implement a new Type for any entity so you can create easily a CRUD form operation in your application. Sometimes, you may want to add dinamically fields into your form from a controller, because you don't want this field to appear on another as it's only needed in this case. This can be achieved thanks to the mapped property of a form field.

Let's imagine that we have an UserType to create a Form to register an user in our application:

<?php

namespace userBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;


class UserType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', TextType::class , array(
                "attr" => array(
                    "class" => "form-control"
                )
            ))
            ->add('username', TextType::class, array(
                "attr" => array(
                    "class" => "form-control"
                )
            ))
            ->add('description', TextareaType::class, array(
                "attr" => array(
                    "class" => "form-control",
                    "maxlength" => 255
                )
            ))
            ->add('password', RepeatedType::class, array(
                'type' => PasswordType::class,
                'invalid_message' => 'The password fields must match.',
                'options' => array(
                    'attr' => array(
                        'class' => 'form-control'
                    )
                ),
                'required' => true,
                'first_options'  => array('label' => 'Password'),
                'second_options' => array('label' => 'Repeat Password'),
            ))
        ;
    }
    
    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'userBundle\Entity\User'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'userbundle_user';
    }
}

This type contains 5 requires fields from an UserEntity that will be processed in our controller to create a new user. In our newAction of the controller we'll create a form with the following code:

<?php

public function newuserAction(Request $request){
    $user = new User();
    
    // Create a form using the UserType
    $form = $this->createForm(UserType::class, $user);
    
    // Add dinamically a new multiple select field that isn't in the UserEntity:
    $form->add('roles_options', ChoiceType::class, array(
        "mapped" => false,
        "multiple" => true,
        "attr" => array(
            'class' => "form-control"
        ),
        'choices'  => array(
            'Blogger' => 'ROLE_BLOGGER',
            'Administrator' => 'ROLE_ADMIN'
        )
    ));

    $form->handleRequest($request);

    if ($form->isSubmitted()) {
        // Rest of the logic
    }

    return $this->render('users/new.html.twig', array(
        'form' => $form->createView()
    ));
}

Now if the user access the new route, he will see a form that he only needs to submit. Once submitted, the same newAction will be triggered (if configured in routing.yml with POST) and your logic to process the form will be executed. The advantages of creating a Form with Symfony Types is that you can persist easily an Entity in your controllers, however you will need sometimes to make the processing step dynamic in the controller by modifying some values (or using them for another purpose) so you can easily retrieve the values of the form easily using the getData method of the form:

<?php

if ($form->isSubmitted()) {
    $data = $form->getData();
    
    // $data  contains an array like:
    // array(
    //    "name" => "The submitted name",
    //    "username" => "The submitted Username",
    //    "description" => "The submitted description",
    //    "password" => "The submitted password"
    // );
}

But wait, where the **** is the value of the roles_options field that we add dinamically in our controller? It isn't inside the real form, simply because the field isn't mapped, so it can't be inside the real form because that would trigger another exception namely 'This form should not contain extra fields'.

Retrieve value of non-mapped field

Instead of access the value from the form array data, you need to use the get method from the form that expects as first argument the name of the field that wasn't mapped. From the returned object, you can retrieve the value using the getData method:

<?php

if ($form->isSubmitted()) {
    $data = $form->getData();

    // Retrieve the value from the extra field non-mapped !
    $roles = $form->get("roles_options")->getData();

    // Where $data  contains an array like:
    // array(
    //    "name" => "The submitted name",
    //    "username" => "The submitted Username",
    //    "description" => "The submitted description",
    //    "password" => "The submitted password"
    // );

    // and $roles another array in this case with our options (that were selected obviously):
    // [
    //   0 => "ROLE_BLOGGER"
    //   1 => "ROLE_ADMIN"
    // ]
}

Happy coding !


Senior Software Engineer at Software Medico. Interested in programming since he was 14 years old, Carlos is a self-taught programmer and founder and author of most of the articles at Our Code World.

Sponsors