Now that we've got our entity let's create a form! Click on AppBundle, press our handy shortcut command+n, and if you search form you'll find that option in the menu! It's all coming together!

We'll call this MovieType:

26 lines src/AppBundle/Form/MovieType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class MovieType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
}
public function configureOptions(OptionsResolver $resolver)
{
}
public function getName()
{
return 'app_bundle_movie_type';
}
}

Notice it was actually smart enough to put that inside of a Form directory and build out the whole structure that we'll need. All I need next is for it to pour me a cup of coffee.

The first thing we always do inside of here is call $resolver->setDefaults(array()) and pass the data_class option so that it binds it to our Movie entity. Conveniently, this gives us more autocomplete: we can just type Movie and it adds the rest:

28 lines src/AppBundle/Form/MovieType.php
... lines 1 - 8
class MovieType extends AbstractType
{
... lines 11 - 15
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Movie'
));
}
... lines 22 - 26
}

Configuring Form Fields

This will even help us build our fields. If we type $builder->add('') here, because this is bound to our Movie entity, it knows all the properties we have there. So let's plug in our property of title which should be a text field, samsCharacterName which is probably a text field as well and isMainCharacter which will be a checkbox. We'll want to make sure that we prevent html5 validation on that. A third argument of 'required' => false will take care of that for us and even that has autocomplete. It's madness!

36 lines src/AppBundle/Form/MovieType.php
... lines 1 - 8
class MovieType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title', 'text')
->add('samsCharacterName', 'text')
->add('isMainCharacter', 'checkbox', array(
'required' => false,
))
... lines 18 - 21
}
... lines 23 - 34
}

Let's also include rating as an integer field and lastly, releasedAt which is a date field:

36 lines src/AppBundle/Form/MovieType.php
... lines 1 - 8
class MovieType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title', 'text')
... lines 14 - 17
->add('rating', 'integer')
->add('releasedAt', 'date', array(
... line 20
));
}
... lines 23 - 34
}

Digging into Options and Fields

We can control how this date field renders: by default with Symfony it would be 3 select boxes. I'll set a widget option, except I don't remember what value to set that to. No worries, I'll just hold the command key over the widget option and it will take me straight to where that is setup inside of the core code:

323 lines vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/DateType.php
... lines 1 - 11
namespace Symfony\Component\Form\Extension\Core\Type;
... lines 13 - 26
class DateType extends AbstractType
{
const DEFAULT_FORMAT = \IntlDateFormatter::MEDIUM;
... lines 30 - 167
public function configureOptions(OptionsResolver $resolver)
{
... lines 170 - 202
$resolver->setDefaults(array(
... lines 204 - 206
'widget' => 'choice',
... lines 208 - 224
));
... lines 226 - 235
$resolver->setAllowedValues('widget', array(
'single_text',
'text',
'choice',
));
... lines 241 - 245
}
... lines 247 - 321
}

Why is that awesome you ask? Because I can search for what I need inside of here and boom setAllowedValues single_text, text and choice. So let's paste single_text back into our file.

36 lines src/AppBundle/Form/MovieType.php
... lines 1 - 8
class MovieType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title', 'text')
... lines 14 - 18
->add('releasedAt', 'date', array(
'widget' => 'single_text'
));
}
... lines 23 - 36

That trick of holding command and clicking works for the field types too: command+click integer and suddenly you're inside the class that provides this!

80 lines vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php
... lines 1 - 11
namespace Symfony\Component\Form\Extension\Core\Type;
... lines 13 - 19
class IntegerType extends AbstractType
{
... lines 22 - 37
public function configureOptions(OptionsResolver $resolver)
{
... lines 40 - 47
$resolver->setDefaults(array(
// deprecated as of Symfony 2.7, to be removed in Symfony 3.0.
'precision' => null,
// default scale is locale specific (usually around 3)
'scale' => $scale,
'grouping' => false,
// Integer cast rounds towards 0, so do the same when displaying fractions
'rounding_mode' => IntegerToLocalizedStringTransformer::ROUND_DOWN,
'compound' => false,
));
... lines 58 - 69
}
... lines 71 - 78
}

It's like being teleported but, you know, without any risk to your atoms. You can even use this to take you to the property inside of Movie for that specific field.

Form Rendering

Our form is setup so let's go ahead and create this inside of our controller, $form = $this->createForm(new MovieType(), $movie);. Like always, we need to pass our form back into our template with $form->createView():

36 lines src/AppBundle/Controller/MovieController.php
... lines 1 - 5
use AppBundle\Form\MovieType;
... lines 7 - 9
class MovieController extends Controller
{
... lines 13 - 15
public function newAction()
{
$movie = new Movie();
$form = $this->createForm(new MovieType(), $movie);
return $this->render('movie/new.html.twig', array(
'quote' => 'If my answers frighten you then you should cease asking scary questions. (Pulp Fiction)',
'form' => $form->createView()
));
}
... lines 27 - 34
}

Time to render this! Click into the new.html.twig template, ah that's right my form is actually going to be over here in _form.html.twig. It shouldn't surprise you that you'll get autocomplete here on things like form_start and form_end:

10 lines app/Resources/views/movie/_form.html.twig
{{ form_start(form) }}
... lines 2 - 8
{{ form_end(form) }}

Want more autocomplete awesomeness you say? You're mad! But ok: type {{ form_row(form) }} and it'll auto-complete the title field for you. So we'll plug in all of our fields here:

10 lines app/Resources/views/movie/_form.html.twig
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_row(form.samsCharacterName) }}
{{ form_row(form.isMainCharacter) }}
... lines 5 - 8
{{ form_end(form) }}

And if I forget one of them, I can hit control+space to bring up all of my options. This will also show you some other methods that exist on that FormView object. Ah ok so we still have rating and releasedAt to add here. Clean up a bit of indentation here and perfect!

10 lines app/Resources/views/movie/_form.html.twig
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_row(form.samsCharacterName) }}
{{ form_row(form.isMainCharacter) }}
{{ form_row(form.rating) }}
{{ form_row(form.releasedAt) }}
<button type="submit" class="btn btn-primary">Save</button>
{{ form_end(form) }}

Time to try this out: back to our new movies page, refresh and there we go! It renders with no problems other than the fact that this is a form only it's developer could love. And well, maybe not even that: I'm going to make it prettier. In config.yml, down in the twig key add form_themes:

83 lines app/config/config.yml
... lines 1 - 34
twig:
... lines 36 - 37
form_themes:
... lines 39 - 83

Now this should autocomplete, but for whatever reason this one key is not doing that. But for the most part, you will see autocompletion inside of your configuration files.

Form Theming

Right here let's plug in the bootstrap form theme: and the plugin isn't perfect because we don't get autocomplete on this either. But I do know that there is a file inside the project for bootstrap, so I'll go to find, file and start typing in bootstrap. We want the one called bootstrap_3_layout.html.twig. To cheat, I'll just copy that file name and paste that in here:

83 lines app/config/config.yml
... lines 1 - 34
twig:
... lines 36 - 37
form_themes:
- bootstrap_3_layout.html.twig
... lines 40 - 83

Refresh with our new form theme and .... Awesome!

Leave a comment!

  • 2016-04-29 weaverryan

    Hi Raphael!

    Ah, thank you so much!!!! I'm glad you love both things :p.

    I might have an easy answer to your question: have you tried the bootstrap_3_horizontal_layout.html.twig form theme? It organizes labels and fields into columns: https://github.com/symfony/sym...

    You can configure to use this form theme in the same way as the other bootstrap form theme.

    Cheers!

  • 2016-04-29 Raphael Schubert

    Hello there!!! i`m in love with Symfony and KNPU... I have a question... There is a way to render the fields of FORM in columns? like [ 'class'=>'col-md-3' ]??? I`m not founding where to change the variables... thanks!!!

  • 2016-03-14 fabienne

    Me again and sorry! Of course I needed to add the reference to the css in base.html.twig....

  • 2016-03-14 fabienne

    Hello, first thanks to Andrew for the solution with the 500 error, I had the same problem.

    I have another question, I have bootstrap under web/vendors/bootstrap, but the code has no effect. Inspect shows that the bootstrap classes are there, but the page looks the same as before, is it a path problem?
    thanks.

  • 2016-03-08 weaverryan

    Ah, so strange! I mostly prefer Chrome because I like its native web inspector better! I *do* (for whatever reason) see it used by most developers, probably with Safari second.

  • 2016-03-07 Andrew Grudin

    Probably my Google Chrome is silly and don't displayt mm/dd/yyyy either.
    From your point of view , is Chrome more advanced for web development now then Fox?
    Best regards!

  • 2016-03-06 weaverryan

    Hey Andrew!

    You're probably doing it correctly :). Symfony renders this fields as an input type="date" field. *Some* browsers (like mine - Chrome) are smart enough to turn that into a "smart" field - that's what you're seeing. I (and Symfony) did *not* add that mm/dd/yyyy - my browser is adding that because it knows this is a date field. If you try Chrome, you will likely see that same behavior. But even if you don't, you can just fill in the field with something like 06/20/2000 and it will submit. If you'd like to have a fancy date picker in *all* browsers, you should use a JavaScript library to help the user fill in the right date (e.g. 06/20/2000).

    Cheers!

  • 2016-03-06 weaverryan

    I literally wrote this and then saw you solved the issue yourself :). Thanks for posting the detailed update! https://knpuniversity.com/scre...

  • 2016-03-06 weaverryan

    Hey Andrew!

    You've already got the right fix actually! This tutorial was on Symfony 2, where we did things like "new MovieType" and ->add('someField', 'text'). In Symfony 3, change it to MovieType::class (you already did this) *and* make a similar change in your form class - e.g. ->add('someField', TextType::class); That'll fix things :).

    Cheers!

  • 2016-03-06 Andrew Grudin

    I did all as you do, but when my form is rendered the field 'Released at' does not display

    'mm/dd/yyyy' inside , just empty. Could you give me, please, any hint how to fix it.

  • 2016-03-04 Andrew Grudin

    I found the issue.

    WAS OK BEFORE(sf 2.8):
    $builder->add('title', 'text')
    ->add('samsCharacterName', 'text')
    ->add('isMainCharacter', 'checkbox', array(
    'required' => false
    ))
    ->add('rating', 'integer')
    ->add('releasedAt', 'date', array(
    'widget' => 'single_text',
    ));

    SHOULD BE (from sf 2.8):

    use Symfony\Component\Form\Extension\Core\Type\DateType;
    use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
    use Symfony\Component\Form\Extension\Core\Type\IntegerType;
    use Symfony\Component\Form\Extension\Core\Type\TextType;
    $builder->add('title', TextType::class)
    ->add('samsCharacterName', TextType::class)
    ->add('isMainCharacter', CheckboxType::class, array(
    'required' => false
    ))
    ->add('rating', IntegerType::class)
    ->add('releasedAt', DateType::class , array(
    'widget' => 'single_text',
    ));

  • 2016-03-03 Andrew Grudin

    When I tried to reload my new form I got exeption:
    Expected argument of type "string", "AppBundle\Form\MovieType" given
    I have changed :
    $form = $this->createForm(new MovieType(), $movie);
    for:
    $form = $this->createForm(MovieType::class, $movie);
    Got new:
    Could not load type "text"
    500 Internal Server Error - InvalidArgumentException
    Your comments suggestions would be greatly appreciated

  • 2016-03-03 weaverryan

    Hi Andrew!

    Do you have the Symfony plugin installed and (more importantly) enabled for this project? That's the most likely cause - let me know! *Sometimes* things won't work, even when you have the plugin installed+enabled, but I don't have problems with forms usually.

    Cheers!

  • 2016-03-03 Andrew Grudin

    I have PHPstorm on my windows7 and symfony 3.
    When I try (Ctrl + click) over 'widget' I do not get to Core/Type/DateType.php as you do but see "Can't find declaration to go to".
    All the same for 'integer'. What can it be?

  • 2016-02-25 weaverryan

    Hmm, I've never seen that before, unfortunately :(. You could try re-installing the plugin/phpstorm - it's the best advice I can give!

  • 2016-02-23 Paulius

    Yes, I have the options but they do nothing :(

  • 2016-02-23 weaverryan

    Hey Paulius!

    So, you have the New->Form and New->Controller options, but they don't do anything when you click them? Or are the options missing? If clicking them does nothing, that sounds like some sort of a bug with the Symfony plugin.

  • 2016-02-23 Paulius

    I'm using PHPStorm on Ubuntu OS.

    When I'm trying to create new Form or Controller nothing happens when I click on 'New->Form' or 'New->Controller'