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

Form Rendering and Form Variables

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

Let's talk form rendering.

Sure, things looks nice right now, especially considering we're rendering all the fields in one line!

The problem? First, we can't change the order of the fields. And second, we won't be able to control the labels or anything else that we're going to talk about.

Using form_row

So, in reality, I don't use form_widget to render all my fields at once. Replace this with a different function: form_row and pass it genusForm. and then the name of one of the fields - like name:

... lines 1 - 22
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
... lines 27 - 28
{{ form_start(genusForm) }}
{{ form_row(genusForm.name) }}
... lines 31 - 37
{{ form_end(genusForm) }}
</div>
</div>
</div>
{% endblock %}

Since this form has 1, 2, 3, 4, 5, 6 fields, I'll copy that and paste it six times. Now, fill in all the field names: subFamily, speciesCount, funFact, isPublished and firstDiscoveredAt:

... lines 1 - 22
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
... lines 27 - 28
{{ form_start(genusForm) }}
{{ form_row(genusForm.name) }}
{{ form_row(genusForm.subFamily) }}
{{ form_row(genusForm.speciesCount) }}
{{ form_row(genusForm.funFact) }}
{{ form_row(genusForm.isPublished) }}
{{ form_row(genusForm.firstDiscoveredAt) }}
... lines 36 - 37
{{ form_end(genusForm) }}
</div>
</div>
</div>
{% endblock %}

Refresh! OMG - it's the exact same thing as before!!! This is what form_widget was doing behind the scenes: looping over the fields and calling form_row.

All the Form Rendering Functions

So, at this point, you're probably asking:

What else can I do? What other functions are there? What options can I pass to these functions?

Look, I don't know! But I bet Google does: search for "form functions twig" to find another reference section called Form Function and Variable Reference.

Ah hah! This is our cheatsheet!

First - we already know about form_row: it renders the 3 parts of a field, which are the label, the HTML widget element itself and any validation errors.

If you ever need more control, you can render those individually with form_widget, form_label and form_errors. Use these instead of form_row, only when you need to.

Form Variables

Now notice, most of these functions - including form_row - have a second argument called "variables". And judging by this code example, you can apparently control the label with this argument.

Listen closely: these "variables" are the most powerful part of form rendering. By passing different values, you can override almost every part of how a field is rendered.

So what can you pass here? Scroll down near the bottom to find a big beautiful table called Form Variables Reference.

This gives you a big list of all the variables that you can override when rendering a field, including label, attr, label_attr, and other stuff.

To show this off, find the speciesCount field and add a second argument set to {} - the Twig array syntax. Override the label variable: set it to Number of Species:

... lines 1 - 22
{% block body %}
<div class="container">
<div class="row">
<div class="col-xs-12">
... lines 27 - 28
{{ form_start(genusForm) }}
... lines 30 - 31
{{ form_row(genusForm.speciesCount, {
'label': 'Number of Species'
}) }}
... lines 35 - 39
{{ form_end(genusForm) }}
</div>
</div>
</div>
{% endblock %}

Refresh! It's just that easy.

The Amazing Form Profiler

There's one huge form tool that we haven't looked at yet. Your web debug toolbar should have a clipboard icon. Click it.

This is the profiler for your form, and it's packed with stuff that's going to make your form life better. If you click speciesCount, you can see the different data for your species - which isn't too interesting on this blank form. You can see any submitted data and all of the variables that can be passed to the field.

It turns out, the reference section we looked at has most of the variables but this will have all of them. This is your place to answer:

What are the values for my variables? And, what can I override?

This also shows you "Resolved Options": these are the final values of the options that can be passed as the third argument to the add() function.

CSRF Protection

Oh, and check out this _token field. Do you remember adding this?

Hopefully not - because we never did! This is a CSRF token that's automatically added to the form. It's rendered for us when we call form_end and validated behind the scenes along with all the other fields. It's free CSRF protection.

Leave a comment!

20
Login or Register to join the conversation
Default user avatar
Default user avatar Peter Stephens | posted 5 years ago

Hmmmm.... No auto complete here?

Reply
Default user avatar

Never mind. It was wrong file.

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

How Do I render a form without a label.. I tried {'label' : ''} but it will give me an error! saying "put some value inside idiot".

Reply

Hey Jian!

Hmm, I haven't got that error message before, but I get the idea ;)
If you don't want to render a label you only have to set to "false" the label attribute, you can do it from the "FormType" class using the "FormBuilder" or inside the template: {'label' : false}

Cheers!

Reply
Default user avatar

great! thank you

Reply
Default user avatar
Default user avatar Jacobo Quiles Reina | posted 5 years ago

Hi! Congratulations, your tutorials are really impresive, easy to follow and understand. Thank you very much!
That being said, i am facing a problem and i can't seem to find a proper solution for it.
Basically,
i would like to display a search form that would filter the list of
elements being displayed by several entity fields.
This i think i did
it correctly, but when i add a paginator, i can't find a way to
properly pass the filters selected in the paginator.
For example, in
the genus project, add a search form by genus name, creation date, etc
in the genus list controller with pagination.
Any guidance will be really appreciated, or maybe a tutorial about this point will be great also.

Thanks in advanced, and excuse my poor english

Reply

Hey Jacobo,

May I know what paginator do you use? Is it an Open Source lib like WhiteOctoberPagerfantaBundle? Generally, you need to create a method in entity repository, build the query which applies filtering. Then you need to pass this query to the paginator and it did the trick.

Cheers!

Reply
Default user avatar
Default user avatar Jacobo Quiles Reina | Victor | posted 5 years ago

Hey Victor, thanks for your reply. Yes, that's kind of what i do. I have a method in the repository that filters and sets the limit and the offset. Then returns a native orm paginator object. Then in the controller, if user click the search button i get the $request->request var, or if used the paginator i get the $request->query var. Then in the template i send the params as get parameters in each paginator link. It looks a bit messy, was wondering if there was any workaround.

Thank you for the help!

Reply

Hey Jacobo,

First of all, generally the GET method is used for search. Actually, Google and GitHub use GET method as well. Then you don't need t worry about POST data, you always will work with $request->query. And this way you can send someone a link to your search results which is impossible with the POST. IIRC, paginators like WhiteOctoberPagerfantaBundle or KnpPaginatorBundle fix this out of the box. So I recommend you to take a look at them, they will make your life a bit easier by rendering the paginator's links with other query parameters you have on the page :)

Cheers!

Reply
Default user avatar
Default user avatar Jacobo Quiles Reina | Victor | posted 5 years ago

Thank you Victor! I will definetly check them out.

Reply

In Web Profiler `View Variables` list `label` variable is null. Shouldn't it be 'Number of species'?

Reply

Hey Serge,

We overwrite label on template level, i.e. setting "Number of species" in admin/genus/new.html.twig - probably that's the key. I think if you set the label on Form Type level - it would be there. Probably this is a bug, or maybe just a some kind of limitations, Im not sure.

Cheers!

1 Reply
Richard Avatar
Richard Avatar Richard | posted 5 years ago

How are you getting code completion for stuff like {{form_row(form.name)}} ? In my twig template it doesn't actually seems to now the form is representing the Person entity (in my data model). Is it down to consistent naming conventions?

Reply
Richard Avatar

I answered it myself by adding {# personForm \AppBundle\Entity\Person #} but your example doesn't have the equivalent for the tutorial's data mode. So how is it working in your code? Does location of the form make a huge difference perhaps?

Reply

Hey Richard

I suppose you are using "PhpStorm", if so I think you already know about the symfony plugin that you can install. I don't know how it works internally but you can configure it in the settings window -> plugins -> symfony. and there is an option for Twig.
I believe it checks your controller actions, and checks which template you are using and which variables you are passing into it, so it can give you autocomplete
They have a documentation if you want to go deeper http://symfony2-plugin.espe...

I hope it may helped you :)

Reply
Default user avatar
Default user avatar Terry Caliendo | posted 5 years ago | edited

You say in the video that when using the form_widget you can't change the order of the fields. However according to my tests it does appear that the order in which you add the fields to the form builder is the order they appear on the form.

Question Title displays First on the Form


        $form = $this->createFormBuilder($FlowObject)
            ->add('QuestionTitle')  
            ->add('QuestionInformation')  
            ->getForm();

VS

Question Title displays 2nd on the Form


        $form = $this->createFormBuilder($FlowObject)
            ->add('QuestionInformation')
            ->add('QuestionTitle') 
            ->getForm();
Reply

Yo Terry!

You're totally right as usual :). You *can* change the order... you just can't do it from inside of the template (only from inside the form class like you added here). This is an "accidental" feature... meaning it's not in purpose that it renders in the same way that you build your fields. That being said, it's not going to change ever, so this *is* a reliable way to control the order of your fields.

The next question is, should you do this? It depends: if you're a one-(wo)man army, maybe! But if you may ever work with frontend people, then the more display logic you can put into the template, the better: we want to simply be able to tell them to open "form.html.twig" to make changes, not also to need to hunt down a form class or controller :).

Cheers!

Reply
Default user avatar
Default user avatar Terry Caliendo | weaverryan | posted 5 years ago

Got it... As always, thanks for the informative response!

Reply
Default user avatar
Default user avatar Helene Shaikh | posted 5 years ago

You can actually change the order of the fields by changing the order of the properties in GenusFormType's buildForm() function

Reply

Hey Helene Shaikh

You are totally right, that's another way to change the order of the fields, but if you are going to reuse that FormType, you can't change it without affecting others

Have a nice day :)

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