Render that Form Pretty (Bootstrap)
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.
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?