Learn how to remove dinamically a field from a FormType in Symfony (from a controller or with options in the Type).

All the fields of a Form Type are usually used everywhere, but there are cases when a field shouldn't appear in some sections of your app for some reasons. Obviously you won't render the fields in the frontend and hide them with CSS, DON'T DO THAT, PLEASE. In such cases you can remove the fields from the Form Type easily so you don't need to create an incomplete Form Type and then write the conditional fields in the controllers.

For example, in this article we are going to use the following FormType namely UserType:

<?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\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;

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 getBlockPrefix()
    {
        return 'userbundle_user';
    }
}

This FormType has 4 fields: name, username, description and password. This form will be used in 2 actions : newAction and editAction. While working in the newAction we won't need to remove any field, however when the user edits, we will need to remove the password field and it can be achieved in two ways:

A. Removing field using the remove method

From the returned object by the createForm method, simply invoke the remove method of the Form class:

<?php

namespace userBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

// Classes of the example
use userBundle\Form\UserType;
use userBundle\Entity\User;


class UserController extends Controller
{
    // .. //

    public function editAction(Request $request, $id){
        $em = $this->getDoctrine()->getManager();
        
        $user = $em->getRepository("userBundle:User")->find($id);
        
        if(!$user){
            throw $this->createNotFoundException("The providen user doesn't exist with id $id");
        }
        
        // Prepare Form
        $editForm = $this->createForm(UserType::class, $user);

        // Remove the password field !
        $editForm->remove('password'); 

        // Rest of the code ...
    }
    
    // .. //
}

This will simply remove the field from the Form and that's it.

B. Filtering using options in the buildForm

As you may (or maybe not) know, the createForm method expects as third argument an array with options: 

/**
 * Creates and returns a Form instance from the type of the form.
 *
 * @param string $type    The fully qualified class name of the form type
 * @param mixed  $data    The initial data for the form
 * @param array  $options Options for the form
 *
 * @return Form
 */
protected function createForm($type, $data = null, array $options = array()){}

that is sent to the buildForm function as second argument in the Form Type class:

public function buildForm(FormBuilderInterface $builder, array $options)

So you can use a simple "flag" argument that specifies (only if defined) to render the password field or not, for example you will need to modify your type and add a conditional with the options:

<?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\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;

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

        // If the usePassword options is set to true,
        if($options["usePassword"]){
            $builder->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'),
            ));
        }
    }
    
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        // Set by default when the options aren't providen, use the password
        $resolver->setDefaults(array(
            'usePassword' => true
        ));
    }

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

Note that you need to use the setDefaultOptions to specify the value when the third argument isn't providen during the creation of the form. And finally in your controller specify an array with the options, that in this case is only ona namely usePassword with a boolean value:

<?php

namespace userBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

// Classes of the example
use userBundle\Form\UserType;
use userBundle\Entity\User;


class UserController extends Controller
{
    // .. //

    public function editAction(Request $request, $id){
        $em = $this->getDoctrine()->getManager();
        
        $user = $em->getRepository("userBundle:User")->find($id);
        
        if(!$user){
            throw $this->createNotFoundException("The providen user doesn't exist with id $id");
        }
        
        // Prepare Form and remove the password field
        $editForm = $this->createForm(UserType::class, $user , [
            'usePassword' => false
        ]);
        
        // Rest of the code ...
    }
    
    // .. //
}

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