Aprende a remover un campo dinámicamente de un formulario (FormType) en Symfony 3.

Todos los campos de un formulario de la instancia Form Type son usados generalmente en todos lados, sin embargo hay casos donde querrás que un campo no aparezca en ciertas areas de tu aplicación por alguna razon. Obviamente no renderizarás todos los campos en el frontend para despues ocultarlos con CSS, NO HAGAS ESO, POR FAVOR. En estos casos puedes remover los campos del formulario fácilmente así que no necesitarás crear un formulario incompleto del tipo Form Type y luego escribir los campos condicionales en los controladores.

Por ejemplo, en este artículo vamos a usar el siguiente FormType llamado 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';
    }
}

Este FormType tiene 4 campos: name, username, description y password. Este formulario será usado en 2 acciones: newAction y editAction. Mientras trabajas en la acción nueva (newAction) no necesitarás remover ningun campo, sin embargo cuando el usuario edita, queremos remover el campo password. Esto puede ser logrado de 2 maneras:

A. Remover campo usando el método remove

Del objeto retornado por el método createForm en el controlador, simplemente usa el método remove de la clase Form que espera como primer argumento el nombre del campo a remover:

<?php

namespace userBundle\Controller;

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

// Clases del ejemplo
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("El usuario con id $id no existe");
        }
        
        // Preparar el formulario
        $editForm = $this->createForm(UserType::class, $user);

        // Remover el campo password !
        $editForm->remove('password'); 

        // Resto del código ...
    }
    
    // .. //
}

Esto simplemente removera el campo del formulario y ya está.

B. Filtrando usando opciones en el método buildForm

Como sabes (o quizás no), el método createForm espera un tercer argumento opcional que es un array con opciones:

/**
 * Crea y devuelve una instancia de formulario con el tipo de la forma.
 *
 * @param string $type    El nombre completo de la clase del tipo de formulario
 * @param mixed  $data    La información inicial del formulario
 * @param array  $options Opciones para el formulario
 *
 * @return Form
 */
protected function createForm($type, $data = null, array $options = array()){}

este es enviado a la función buildForm como segundo argument en la instancia de tu formulario Form Type:

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

Así que puedes usar simplemente una "bandera" que especifica (solo si definida) si se debe renderizar el campo password o no, por ejemplo necesitarás modificar tu UserType y agregar una condición con la opción:

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

        // Si la opcion usarPassword es igual a true
        if($options["usarPassword"]){
            $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(
            'usarPassword' => true
        ));
    }

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

Nota que necesitarás colocar el valor por defecto de la opción en la función setDefaultOptions para especificar el valor del tercer argumento no es usado durante la creación del formulario. Finalmente en tu controlador especifica un array con opciones, que en este caso es solamente una opción llamada usarPassword con un valor booleano:

<?php

namespace userBundle\Controller;

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

// Clases del ejemplo
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("El usuario con id $id no existe");
        }
        
        // Preparar formulario y remover campo password
        $editForm = $this->createForm(UserType::class, $user , [
            'usarPassword' => false
        ]);
        
        // Resto del código ...
    }
    
    // .. //
}

Que te diviertas !


Ingeniero de Software Senior en EPAM Anywhere. Interesado en la programación desde los 14 años, Carlos es un programador autodidacta, fundador y autor de la mayoría de los artículos de Our Code World.

Conviertete en un programador más sociable

Patrocinadores