Reusing and Putting Data Back In To Symfony Forms


In this video we are continuing on from the introduction to Symfony forms from the previous lesson, and building on that by looking at more real world form usage.

By the end of this video you will have seen how to create re-usable forms, how re-usable forms differ from in-line forms, and also how to populate a form with data - for example if you were reloading a blog post from a database and wanted to be able to edit that blog post using the same form that you used to initially create the post.

As I mentioned in previous video, it is better practice to define our forms in a way that makes them reusable.

We don't want to end up in a situation where we have to define all our form fields for adding data, then define them all again for editing data, and so on. That isn't very DRY (Don't Repeat Yourself) at all, no siree Bob.

Right out of the box, Symfony is set up to encourage form re-use. If you look at the example project as we did all the way back in the first video, you will see the same pattern in use there, and many / most other projects that use Symfony 2.

All the Hype is in the Type

We define our forms inside the src/YourBundle/Form/Type directory, and we call our forms FormNameType.php.

This is a little odd.

You'll see this word 'type' appear frequently when talking about the form.

As mentioned, we have the FormType.

When we create our form class, we extend AbstractType.

AbstractType implements the FormTypeInterface. (Check out the Symfony 2.7 API docs if you want to dig deeper here)

All our fields have a Type (text, textarea, choice, etc).

When it comes to forms, the word Type is everywhere.

Using and Reusing Symfony Form Types

In my opinion, the official documentation does a great job of explaining how to use the form.

It also does a great job of explaining how to set up re-usable forms, or form classes.

Also, I show this in the video.

Rather than cover this again, I want to go over what I found to be one of the most confusing parts of using the form.

Let's take an example:

// src/AppBundle/Controller/YourController.php

// make sure we have the appropriate use statement
use AppBundle\Form\Type\MyFormType;

// class YourController etc 

    public function someFormAction()
    {
        $form = $this->createForm(new MyFormType(), $myFormData);
    }

There's something about this syntax that is not beginner friendly.

The good news is, use it a few times and it becomes less scary.

But at first, confusion city.

In the previous video we made use of $this->createFormBuilder(), but here we have changed ever so slightly to just using $this->createForm().

Why have we done this?

Well, in the previous video we were defining our entire form right there in the controller action.

The form builder ($this->createFormBuilder()) helps us create a form on the fly.

A standalone form class (e.g. src/AppBundle/Form/Type/MyFormType.php) will still use a builder, but we shouldn't need to know about how a form is built inside in our controller action:

// src/AppBundle/Form/Type/SomeFormType.php

    // notice we use FormBuilderInterface $builder 
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('task')
            ->add('dueDate', null, array('widget' => 'single_text'))
            ->add('save', 'submit')
        ;
    }

The form builder is the interface we work with to tell Symfony which form fields we need.

When we used the $this->createFormBuilder() / inline form approach we had to make sure to call ->getForm(); as the last method in our builder chain.

When we use $this->createForm(), the ->getForm(); method is still called, but it's called behind the scenes by Symfony on our behalf.

This is a minor nuance really, rather than something to be concerned about on a daily basis.

Create Form, Apply Data

Getting back to the example:

    $form = $this->createForm(new MyFormType(), $myFormData);

Here, the $form variable contains a combination of the form type (think: the form blue print or template (but not a template in the same sense as a Twig template)), and our data mapped over the top of the form.

First we say - ok, get me the empty form based on the given form type: new MyFormType().

Second, we say - ok, given that we have a new / empty form that expects some data, then populate this form with this data ($myFormData).

As a result, $form contains the form type with its fields containing the data from our entity.

It's a strange one really. Once you have been using Symfony for a while, this is very much second nature, but when I speak with people new to the framework, or sit with them during a training session, it's always a point that seems to raise questions.

Whilst read only now, the Symfony forums contains an interesting post on this subject. It's old, but it's still relevant.

Episodes