Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Using an External Form Type Class

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

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!

16
Login or Register to join the conversation
Default user avatar
Default user avatar Diego A. | posted 5 years ago

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!

Reply

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!

Reply
Default user avatar

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.

Reply

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

Cheers!

Reply

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

Reply

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/curr...

Cheers!

Reply

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

Reply

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!

2 Reply

Great!
thanks for your time bro

Reply
Default user avatar

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

// ...
}

Reply

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

Thanks Marco!

Reply
Default user avatar
Default user avatar yujunjie0603 | posted 5 years ago

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

thanks

Reply

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!

1 Reply
Default user avatar
Default user avatar yujunjie0603 | posted 5 years ago

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 !

Reply

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/s...

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!

1 Reply

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

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "~2.4", // v2.4.2
        "doctrine/orm": "~2.2,>=2.2.3", // v2.4.2
        "doctrine/doctrine-bundle": "~1.2", // v1.2.0
        "twig/extensions": "~1.0", // v1.0.1
        "symfony/assetic-bundle": "~2.3", // v2.3.0
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.5
        "symfony/monolog-bundle": "~2.4", // v2.5.0
        "sensio/distribution-bundle": "~2.3", // v2.3.4
        "sensio/framework-extra-bundle": "~3.0", // v3.0.0
        "sensio/generator-bundle": "~2.3", // v2.3.4
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "doctrine/doctrine-fixtures-bundle": "~2.2.0", // v2.2.0
        "ircmaxell/password-compat": "~1.0.3", // 1.0.3
        "phpunit/phpunit": "~4.1" // 4.1.0
    }
}
userVoice