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

Render that Form Pretty (Bootstrap)

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Head into the GenusAdminController. I gave us a tiny head start - there's already a newAction():

... lines 1 - 11
class GenusAdminController extends Controller
{
... lines 14 - 27
/**
* @Route("/genus/new", name="admin_genus_new")
*/
public function newAction()
{
... lines 33 - 37
}
}

In the admin area, the "Add" button points here. Click it!

The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller?

Yep, a great explosion!

Instantiating the Form Object

To create the form object, add $form = $this->createForm(). This is a shortcut method in the base Controller that calls a method on the form.factory service. That's my friendly reminder to you that everything - including form magic - is done by a service.

To createForm(), pass it the form type class name - GenusFormType::class:

... lines 1 - 4
use AppBundle\Form\GenusFormType;
... lines 6 - 11
class GenusAdminController extends Controller
{
... lines 14 - 30
public function newAction()
{
$form = $this->createForm(GenusFormType::class);
... lines 34 - 37
}
}

And because I used autocomplete, that did just add the use statement for me. The ::class syntax is new in PHP 5.5 - and we're going to use it a lot.

Rendering the Form

Now that we have a form object, just render a template a pass it in: return $this->render() with admin/genus/new.html.twig to somewhat follow the directory structure of the controller:

... lines 1 - 11
class GenusAdminController extends Controller
{
... lines 14 - 30
public function newAction()
{
$form = $this->createForm(GenusFormType::class);
return $this->render('admin/genus/new.html.twig', [
... line 36
]);
}
}

Pass in one variable genusForm set to $form->createView():

... lines 1 - 11
class GenusAdminController extends Controller
{
... lines 14 - 30
public function newAction()
{
$form = $this->createForm(GenusFormType::class);
return $this->render('admin/genus/new.html.twig', [
'genusForm' => $form->createView()
]);
}
}

Don't forget that createView() part - it's something we'll talk about more in a future course about form theming.

Hold command+click to jump into the template. Yep, I took the liberty of already creating this for us in the app/Resources/views/admin/genus directory:

{% extends 'base.html.twig' %}
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>New Genus</h1>
... lines 8 - 13
</div>
</div>
</div>
{% endblock %}

Here, we know we have a genusForm variable. So... how can we render it? You can't render! I'm kidding - you totally can, by using several special Twig functions that Symfony gives us.

The Form Twig Functions

First, we need an opening form tag. Render that with form_start(genusForm):

... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>New Genus</h1>
{{ form_start(genusForm) }}
... lines 10 - 13
</div>
</div>
</div>
{% endblock %}

To add a closing form tag, add form_end(genusForm):

... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>New Genus</h1>
{{ form_start(genusForm) }}
... lines 10 - 12
{{ form_end(genusForm) }}
</div>
</div>
</div>
{% endblock %}

I know, having functions to create the HTML form tag seems a little silly. But, wait! form_start() is cool because it will add the enctype="multipart/form-data" attribute if the form has an upload field. And the form_end() function takes care of rendering hidden fields. So, these guys are my friends.

Between them, render all three fields at once with form_widget(genusForm):

... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>New Genus</h1>
{{ form_start(genusForm) }}
{{ form_widget(genusForm) }}
... lines 11 - 12
{{ form_end(genusForm) }}
</div>
</div>
</div>
{% endblock %}

And finally, we need a button! We can do that by hand: <button type="submit">, give it a few Bootstrap classes and call it "Save":

... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>New Genus</h1>
{{ form_start(genusForm) }}
{{ form_widget(genusForm) }}
<button type="submit" class="btn btn-primary">Save</button>
{{ form_end(genusForm) }}
</div>
</div>
</div>
{% endblock %}

Tip

You can also add buttons as fields to your form. But this is helpful in very few cases, so I prefer just to render them by hand.

that's it! Head to the browser and refresh.

There it is! A rendered form with almost no work. They're all text boxes now: we'll customize them soon.

The Bootstrap Form Theme

Of course, it is pretty ugly... Symfony has default HTML markup that it uses to render everything you're seeing: the labels, the inputs and any validation errors.

Since we're using Bootstrap, it would be really cool if Symfony could automatically render the fields using Bootstrap-friendly markup.

Yep, that's built-in. Open app/config/config.yml. Under twig, add form_themes and then below that - bootstrap3_layout.html.twig. Actually, make that bootstrap_3_layout.html.twig:

... lines 1 - 36
# Twig Configuration
twig:
... lines 39 - 42
form_themes:
- bootstrap_3_layout.html.twig
... lines 45 - 74

Form themes are how we can control the markup used to render forms. The bootstrap_3_layout.html.twig template lives in the core of Symfony and now, our form markup will change to use HTML bits that live inside of it.

Try it out. Beautiful. Now, let's submit this form and do something with its data.

Leave a comment!

22
Login or Register to join the conversation
Default user avatar

Hello
Please, help me.
I want to use bootstrap for my own project for the form.
How I could to add bootstrap to a symfony app correctly?

Reply

Hi Nina!

That is actually what we discuss in this chapter! :) https://knpuniversity.com/s...

By following this, the form will render with bootstrap-friendly classes. We added the Bootstrap CSS back in the first episode of this series: https://knpuniversity.com/s...

I hope that helps! Cheers!

Reply
Default user avatar
Default user avatar Osshe Louis | posted 5 years ago

Hi, I had this issue where the textbox stay the same even after configuring the form_themes on the config.yml

Any similar encounter or solutions to this?

Reply

Hey Osshe

Does only the textbox stays the same but other fields changed ?
Could you show me how your config.yml and form html looks like ?

Have a nice day!

Reply
Default user avatar

Form html

{% block body %}

<div class="container">
<div class="row">
<div class="col-xs-12">
<h1>New Genus</h1>

{{form_start(genusForm)}}
{{form_widget(genusForm)}}

<button type="submit" class="btn btn-primary">Save</button>

{{form_end(genusForm)}}

</div>
</div>
</div>
{% endblock %}

config yml (only twig part)

# Twig Configuration
twig:
debug: "%kernel.debug%"
strict_variables: "%kernel.debug%"
number_format:
thousands_separator: ','
form_themes:
- bootstrap_3_layout.html.twig'

Reply

Hmm, everything looks good, but looks like you have an extra ' (single quote) after bootstrap_3_layout.html.twig
try removing it


form_themes: 
- bootstrap_3_layout.html.twig

Cheers!

Reply
Default user avatar

I figure it out yesterday, i was running the code on "prod"

Thanks for the help anyway

Reply

I'm glad to hear you could fix it
One more thing, if you are working in prod environment, don't forget to clear your cache :)

Have a nice day!

Reply
Default user avatar
Default user avatar jian su | posted 5 years ago

Our UX designer doesn't happy about the bootstrap theme. she want to use her own CSS style for the form. how can we do that?

Reply

Hey Jian,

If you don't need Bootstrap styles - do not import it at all, I mean do not add this line:


# app/config/config.yml
twig:
    form_themes:
        - bootstrap_3_layout.html.twig

Instead, create a new CSS file with your custom styles in the web/ directory and then link it in your base Twig template with the "link" tag.

Cheers!

Reply
Default user avatar

Got it! :)

Reply

I don't understand the purpose of using form_widget inside twig, why do we need to use it?

{{ form_start(login_form) }} this will generate all the form with its fields, labels etc...

The following code works fine with or without form_widget:


{{ form_start(login_form) }}
{#{{ form_widget(login_form) }}#} 
{{ form_end(login_form) }}

Note: As I see form_widget will only append a &lt;div&gt; tag to the current form:


&lt;div id="login_form"&gt;

Am I wrong or ?

Reply

Hey Ahmad,

Actually, when you commented out `form_widget(login_form)`, all fields will be rendered with `form_end(login_form)`. Before rendering the close form tag, Symfony will render all remaining fields, which have not been rendered yet. So it works without `form_widget(login_form)` just fine, but it could confusing other developers when they see an empty form.

And by the way, `form_widget(login_form)` do a bit more extra works - it renders all the form fields, which includes the field element itself, a label and any validation error messages for the field. But if you render a separate field with `form_widget` like `{{ form_widget(login_form._username) }}`, it'll render only HTML representation of a field without label and errors, so you should use `form_label` and `form_errors` too, or simply use `form_row` instead of `form_widget`.

Cheers!

1 Reply
Dan_M Avatar

Hey guys! I love your tutorials. I'm working on my first symfony app and going through the tutorials in parallel with my own site development. My development environment doesn't seem to have bootstrap as part of symfony, and I don't know how to get it installed. Can you give me some direction?

Thanks!

Reply

Hey Dan_M

It's great that you are working on something real while watching our tutorials :)
Can you tell if you are using Symfony Standard Edition ? because it should have bootstrap layout's builted-in
Now, if you what you mean is bootstrap itself, you have to include it into your base template, so every template can make use of it, there are several ways to include it into your project, give a look here http://getbootstrap.com/get...

I hope it helps you :) cheers!

Reply
Dan_M Avatar

Thanks! That helped a lot.

As it turns out, I was really close. The getbootstrap.com link got me over the finish line on this problem.

Reply
Default user avatar

Hi Ryan,
you suggest to create the buttons by hand and in twig. suppose that we have a form with two buttons. one for uploading related files to a form and the other one responsible for submitting the whole form. I want to disable the validation for the upload button so I don't get validation errors while uploading files. if this button was created in FormType class, I could easily set the validation_groups to false. How would I do that when I create the both buttons in twig. Is this possible?

Reply

Hey Mehdi

Just to clarify why is a good practice to render submit buttons by hand is because you can reuse your forms in other places

Your second button (for uploading files), how are you managing? I mean, when you click it, what happens, does it adds a new file input to the form?

Cheers!

Reply
Default user avatar

Thanks,
No the file input is already in the form. This is the behavior that I expect: when I select a file and click the upload button, the file is uploaded without receiving validation errors regarding other fields in the form.

Reply

Oh, I got you know! In that case, I think you have to separate it into another form, so you can create an endpoint for each action, and handle them independently.

Reply
Default user avatar

So, I'm getting to it and my first app is underway. However, this afternoon I struggled with translating (making my app multilangual since my native language is not English) and I didn't get it to work. Maybe you could have a course about internationalization? How to translate my hard coded stuff (like headers), my form labels, my Doctrine fields but also my settings on the site like date format and thousand seperator?

The docs are not clear to me. They fail to tell me where to put stuff (in myController in my.html.twig in myEntity) and then they advise me to use the locale like 'en_US', but all examples just say 'en'...

Reply

Hey Hermen!

Well first, congrats on your first app! A course about translations, etc is on our list, but let me see if I can help you out a bit now. Translations, or "internationalization" is broken down into 3 separate pieces:

A) Translating the static strings (like headers and form labels). This is pretty easy, it involves doing things like {{ 'welcome'|trans }} in your Twig files (or slightly different code if you need to translate something in your controller). Then, you need to create a messages.en.yml file and other files for other languages message.de.yml. You don't need to use the en_US long format, unless you need to have 2 translations for the same language, but for different countries (e.g. British vs UK English).

B) Translating your data inside Doctrine is an entirely different task (and you should make sure you need this - it's a lot of extra maintenance to need to translate everything you put into the database). For this, https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html and then following along with the docs in the Gedmo library it integrates: https://github.com/Atlantic18/DoctrineExtensions/blob/v2.4.x/doc/translatable.md. I've never had to do this before, I'm sure there are some pitfalls and possibly some gaps in the docs!

C) Formatting dates and currencies is the third and final piece. If I remember correctly, I don't think that Symfony gives you much here, because PHP has all of this functionality (Symfony forms do have a few spots where they need to handle this for you, but they use the underlying PHP functions behind the scenes). For example, for the thousands separator, you'll use http://php.net/manual/en/function.number-format.php or http://php.net/manual/en/class.numberformatter.php. For dates, I think you'll use this (I'm not an expert on this stuff!) http://us3.php.net/manual/en/function.strftime.php. These functions honestly have always been a bit tricky for me to understand, but they are the right way to handle this.

Anyways, I hope that at least gets you going!

Cheers!

Reply
Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}
userVoice