Buy

Using an External Form Type Class

We built our form right inside the controller, which was really simple. But, it makes our controller a bit ugly and if we needed to re-use this form somewhere else, that wouldn’t be possible.

For those reasons, the code to create a form usually lives in its own class. Create a new Form directory and a new file called RegisterFormType. Create the class, give it a namespace and make it extend a class called AbstractType:

// src/Yoda/UserBundle/Form/RegisterFormType.php
namespace Yoda\UserBundle\Form;

use Symfony\Component\Form\AbstractType;

class RegisterFormType extends AbstractType
{
}

We need to add a few methods to this class. The first, and least important is getName(). Add this, and just return a string that’s unique among your forms. It’s used as part of the name attribute on your rendered form:

// src/Yoda/UserBundle/Form/RegisterFormType.php
// ...

public function getName()
{
    return 'user_register';
}

The really important method is buildForm(). I’m going to use my IDE to create this method for me. If you create your’s manually, just don’t forget the use statement for the FormBuilderInterface.

The buildForm method is where we build our form! Genius! Copy the code from our controller that adds the fields and put that here:

// src/Yoda/UserBundle/Form/RegisterFormType.php

use Symfony\Component\Form\FormBuilderInterface;
// ...

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('username', 'text')
        ->add('email', 'email')
        ->add('plainPassword', 'repeated', array(
            'type' => 'password'
        )
    );
}

Finally, create a setDefaultOptions function and set the data_class option on it:

// src/Yoda/UserBundle/Form/RegisterFormType.php

use Symfony\Component\OptionsResolver\OptionsResolverInterface;
// ...

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Yoda\UserBundle\Entity\User',
    ));
}

That’s it! This class is now a recipe for exactly how the form should look.

Using the Form Class

Remove the builder code in our controller. Instead, replace it with a call to createForm and pass it an instance of the new RegisterFormType:

// src/Yoda/UserBundle/Controller/RegisterController.php

use Yoda\UserBundle\Form\RegisterFormType;
// ...

public function registerAction(Request $request)
{
    $defaultUser = new User();
    $defaultUser->setUsername('Foo');

    $form = $this->createForm(new RegisterFormType(), $defaultUser);

    // ...
}

Refresh! We’ve conquered forms!

Forms: From Space

Some quick review. A form is something you build, giving it the fields and field types you need. At first, a form just returns an associative array, but we can change that with the data_class option to return a populated object. Forms can also be built right inside the controller or in an external class.

Got it? Great! Let’s move on to validation.

Leave a comment!

  • 2016-04-05 Diego Aguiar

    Hey there!

    If someone is going thru this tutorial but using symfony 3, you will find a little problem with "createForm" method, it now takes the class name (RegisterFormType::class) instead of the object itself

  • 2015-12-08 weaverryan

    Hmm, that is very strange! You should not need to change from "user_register" to "form" - in fact the getName() method is not very important.

    I think there could be another problem. What does your Twig template look like? The CSRF token is a hidden input field, and if you forget to render it, you will get this error message. Make sure you have form_rest(form) in your Twig template (or in newer versions of Symfony, you can use form_end(form) - look at the *last* code block on this page: https://knpuniversity.com/scre...

    Ultimately, if you view the HTML source of your page, you should see name="_token" field. If you don't see that, the CSRF token is not rendering, which will cause you this problem :).

    Cheers!

  • 2015-12-08 weaverryan

    Hey there!

    Sorry for the late reply! You're right that buildForm returns nothing :). But that's ok! It's purpose is to modify the `$builder` variable that is passed in. So basically:

    1) Symfony creates this `$builder` variable
    2) Symfony calls buildForm() and passes you this variable
    3) You modify this by adding fields with add()
    4) Symfony uses the modified `$builder` to build your form

    Does that make sense?

    Cheers!

  • 2015-12-02 yujunjie0603

    I encountered another problem,

    when i submit, it displays :

    The CSRF token is invalid. Please try to resubmit the form.

    but if in the getName, it return "form" replace the "user_register",

    there is not this problem !

    like this

    class RegisterFormType extends AbstractType
    {
    public function getName()
    {
    return "form";
    }

    ...
    }
    could you help me plz ? merci !

  • 2015-12-01 yujunjie0603

    hello, there are something about buildForm that i don't understand, buildForm return nothing, how do it work plz ?

    thanks

  • 2015-02-09 Diego Aguiar

    Great!
    thanks for your time bro

  • 2015-02-08 weaverryan

    Ok, so *most* options are included in the docs :). If you want to look in the source code to find the available options, the trick is that every field type is backed by a class. For example, "date" is DateType, and you can look on that class for the options (there is also a hierarchy, so all fields extend from FormType and get *its* options too).

    Now, constraints is special - it's added to *all* fields via a "plugin" of sorts, called a "form type extension". To see where these options come from, the class is called FormTypeValidatorExtension. If you look up how form type extensions work and what they do, it'll help make more sense. So it's a powerful bit of functionality that provides these options, but this should get you started.

    Cheers!

  • 2015-02-07 Diego Aguiar

    Oh yeah I found that and it is very useful but my real problem is since I'm working with field validations, I found that you can add constraints directly in FormType, just like this:

    $builder->add('plainPassword', 'repeated', array(
    'constraints' => array(
    new Assert\Regex(array(
    'pattern' => '/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$/'
    ))));

    So I've been searching where can I check all this options but I just can't find it.

    Sometimes I find kind of lost in the documentation hehehe

    Thanks for your fast answer ;]

  • 2015-02-07 weaverryan

    Hey!

    Every field type has different options (though many options - like label - are used by every field type). Just find the field your working with and all the types are doc'ed :) http://symfony.com/doc/current...

    Cheers!

  • 2015-02-07 Diego Aguiar

    Hi there!

    Sorry for asking this dumb question here, but I couldn't find in documentation the list of options to pass to
    [FormBuilderInterface] $builder->add() method (third parameter)

    Thanks for your time :)

  • 2015-01-13 weaverryan

    You're right - I've got a bug in the system - on my list to fix it!

    Cheers!

  • 2015-01-13 Diego A.

    Thanks for your fast answer, and one more thing, I think the comment's counter of the page is broken or something, it says 0 comments.

  • 2015-01-13 weaverryan

    Hi Diego!

    Yes, very good catch! And actually, I don't believe we have a a non-deprecated way to handle this yet. It's something we're working on :). For now, don't worry about it.

    Cheers!

  • 2015-01-13 Diego A.

    Hi, I've following this videos and I found em great, but just one thing, "OptionsResolverInterface" is deprecated in 2.6 version. Would be nice to read a post or watch an update of how to fix it.
    Cheers!

  • 2014-12-18 weaverryan

    Good catch! I just fixed this at sha: https://github.com/knpuniversi...

    Thanks Marco!

  • 2014-12-13 Guest

    Hi , the second argument is $defaultUser not $user...

    public function registerAction(Request $request)
    {
    $defaultUser = new User();
    $defaultUser->setUsername('Foo');

    $form = $this->createForm(new RegisterFormType(), $user);

    // ...
    }