This tutorial has a new version, check it out!

Form Rendering

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

Form Rendering

Create an HTML form tag that submits right back to the same route and controller we just created. The easiest way to render a form is all at once by using a special form_widget Twig function. Give it the form variable that we passed into the template, and add a submit button:

{# src/Yoda/UserBundle/Resources/views/Register/register.html.twig #}
{# ... #}

{% block body %}
<section class="main-block">
    <article>
        <section>
            <h1>Register</h1>

            <form action="{{ path('user_register') }}" method="POST">
                {{ form_widget(form) }}

                <input type="submit" class="btn btn-primary pull-right" value="Register!" />
            </form>
        </section>
    </article>
</section>
{% endblock %}

Form versus FormView

Refresh the page. Woh man, another error!

An exception has been thrown during the rendering of a template (“Catchable Fatal Error: Argument 1 passed to Symfony\Component\Form\FormRenderer::searchAndRenderBlock() must be an instance of Symfony\Component\Form\FormView, instance of Symfony\Component\Form\Form given, called in ...”)

This one is more difficult to track down, but it does have one important detail: Specifically:

Argument 1 passed to FormRenderer::searchAndRenderBlock() must be an instance of FormView, instance of Form given

That’s a lot of words but it means that somewhere, we’re calling a method and passing it the wrong type of object. It’s expecting a FormView, but we’re passing it a Form. Something is wrong with the form we created. Head back to RegisterController to fix this. Whenever you pass a form to a template, you must call createView on it. This transforms the object from a Form into a FormView:

// src/Yoda/UserBundle/Controller/RegisterController.php

public function registerAction()
{
    // ...

    return array('form' => $form->createView());
}

This isn’t important... just remember to do it. The error is a bit tough, but now you know it!

Refresh now and celebrate. We have a fully operational and rendred form. Actually, we haven’t written any code to handle the form submit - that’ll come in a minute.

Being event Lazier with form() and button

There’s an even easier way to render the form, and while I don’t love it, I want you to see it. Head to the Forms Chapter of the docs. Under the Rendering the Form section, it renders it with just one line:

{{ form(form) }}

This displays the fields, the HTML form tag and even a submit button, if you configure one. If you scroll up, you’ll see that the form has a save field that’s a submit type:

$form = $this->createFormBuilder($task)
    // ...
    ->add('save', 'submit')
    ->getForm();

I’m happy rendering the HTML form tags and the submit buttons myself, but you will see this rendering syntax, and I don’t want you to be confused. It’s all basically doing the same thing.

Rendering the Form one Field at a Time

In reality, rendering the form all at once probably won’t be flexible enough in most cases. To render each field individually, use the form_row function on each of your fields:

{# src/Yoda/UserBundle/Resources/views/Register/register.html.twig #}
{# ... #}

<form action="{{ path('user_register') }}" method="POST">
    {{ form_row(form.username) }}
    {{ form_row(form.email) }}
    {{ form_row(form.password) }}

    <input type="submit" class="btn btn-primary pull-right" value="Register!" />
</form>

Refresh the page and inspect the form. Each field row is surrounded by a div and contains the label and input:

<div>
    <label for="form_username" class="required">Username</label>
    <input type="text" id="form_username" name="form[username]" required="required" />
</div>
<!-- ... -->

Using form_widget, form_label and form_errors

In the next screencast, we’ll learn how to customize how a field row is rendered. But even now, we can take more control by using the form_label, form_widget and form_errors functions individually. Let’s try it on just the username field:

{# src/Yoda/UserBundle/Resources/views/Register/register.html.twig #}
{# ... #}

<form action="{{ path('user_register') }}" method="POST">
    <div class="awesome-username-wrapper">
        {{ form_errors(form.username) }}
        {{ form_label(form.username) }}
        {{ form_widget(form.username) }}
    </div>

    {{ form_row(form.email) }}
    {{ form_row(form.password) }}

    <input type="submit" class="btn btn-primary pull-right" value="Register!" />
</form>

form_row just renders these 3 parts automatically, so this is basically the same as before. I usually try to use form_row whenever possible, so let’s change the username back to use this.

Don’t forget form_errors and form_rest!

Apart from the fields themselves, there are two other things that should be in every form. First, make sure you call form_errors on the entire form object:

{# src/Yoda/UserBundle/Resources/views/Register/register.html.twig #}
{# ... #}

<form action="{{ path('user_register') }}" method="POST">
    {{ form_errors(form) }}

    {{ form_row(form.username) }}
    {{ form_row(form.email) }}
    {{ form_row(form.password) }}

    <input type="submit" class="btn btn-primary pull-right" value="Register!" />
</form>

Most errors appear next to the field they belong to. But in some cases, you might have a “global” error that doesn’t apply to any one specific field. It’s not common, but this takes care of rendering those.

Next, add form_rest. It renders any fields that you forgot:

{# src/Yoda/UserBundle/Resources/views/Register/register.html.twig #}
{# ... #}

<form action="{{ path('user_register') }}" method="POST">
    {{ form_errors(form) }}

    {{ form_row(form.username) }}
    {{ form_row(form.email) }}
    {{ form_row(form.password) }}

    {{ form_rest(form) }}

    <input type="submit" class="btn btn-primary pull-right" value="Register!" />
</form>

In addition to that, form_rest is really handy because it renders any hidden fields automatically.

All forms have a hidden “token” field by default to protect against CSRF attacks. With `form_rest`, you never have to worry or think about hidden fields.

We talk more about these functions in future episodes, but under the reference section of Symfony’s documentation, there’s a page called Twig Template Form Function and Variable Reference that mentions all of these functions and how to use them.

Leave a comment!

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
    }
}