Dealing with Forms in symfony

Forms handling, processing, and validation are some of the most common things that we do when we create a web application, and practically, the only way we can request data from a website visitor or user. Handling forms effectively was always one of the most requested features in all modern frameworks. In this chapter, we will go deeply into the process of form creation, validation, and saving data.

Form creation

Symfony2 uses the form component to make the creation of forms easier. The form component is a standalone library and can be used independently, but integration with Symfony2 is very good and seamless.

We have two ways to generate forms. The quicker one is to define forms directly within the controller. While this is the quickest way to create a form, it is not the most elegant one. Forms created in this way are not easy to refactor and are not reusable.

For example, to add a new task to our list, we could define the following form in our task controller in src/AppBundle/Controller/TaskController.php:

<?php

namespace AppBundleController;

use AppBundleEntityTask;
use SymfonyBundleFrameworkBundleControllerController;
use SymfonyComponentHttpFoundationRequest;

class TaskController extends Controller
{
    public function listAction(Request $request)
    {
        $em = $this->getDoctrine()->getManager();

        $tasks = $em->getRepository('AppBundle:Task')
            ->createQueryBuilder('t')
            ->where('t.finished = :finished')
            ->orderBy('t.due_date', 'ASC')
            ->setParameter('finished', false)
            ->getQuery()
            ->getResult();
  
        $newTask = new Task();

        $form = $this->createFormBuilder($newTask)
            ->add('name', 'text')
            ->add('notes', 'text')
            ->add('dueDate', 'date')
            ->add('add', 'submit', ['label' => 'Add Task'])
            ->getForm();

        return $this->render('task/list.html.twig', [
            'tasks' => $tasks,
            'form' => $form->createView()
        ]);
    }
}

Our next step is to set up some default values. The easiest way to add some default values is to add them to the entity constructor. Add the following code to src/AppBundle/Entity/Task.php:

/**
 * @ORMEntity
 * @ORMTable(name="todo_task")
 */
class Task
{
    /* ... */

    /**
     * Constructor
     */
    public function __construct()
    {
        // tags value is auto-generated
        $this->tags = new DoctrineCommonCollectionsArrayCollection();

        // adding default values
        $this->due_date = new DateTime('+1 day');
        $this->created_at = new DateTime();
        $this->finished = false;
    }

In our view in app/Resources/views/task/list.html.twig, we can simply add the following:

{% extends '::base.html.twig' %}

{% block body %}
   {# ... table defined in previous chapter #}
   </table>

    <p>New task:</p>
    {{ form(form) }}
{% endblock %}

Now, when you refresh your page, below the list of tasks you should see the new form element. The preceding code created a new form type within the controller, and we used form Twig helper to render the whole form at once.

Note

Note that this way of rendering a form as presented in the preceding code snippet is very easy, but not very flexible. We will discuss more options to render forms further in this chapter.

This approach is simple, though it’s often better to create a separate form. It is not recommended to have huge controller classes. Also, separating a form makes it easier to refactor and reuse the code in future.

Now let’s try to create the same form, but in a more elegant way. In Symfony2, these form classes are called form types and usually, are placed in FormType within the bundle. Let’s create a new file, src/AppBundle/Form/Type/TaskType.php, with the following content:

<?php

namespace AppBundleFormType;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;

class TaskType extends AbstractType
{
    public function buildForm(
        FormBuilderInterface $builder, array $options
  ) {
        $builder
            ->add('name')
            ->add('notes')
            ->add('due_date')
            ->add('add', 'submit', ['label' => 'Add Task'])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundleEntityTask',
        ]);
    }

    public function getName()
    {
        return 'task';
    }
}

This is basically the same form that we created previously. There are a few differences, though:

  • The form type class needs to have a getName method to define the form namespace. This value will be used to contain form fields within this namespace.
  • We can set up default options within such a class. Here, we can define the data class (the class used to serialize data into, or define some form options like CSRF protection or validation groups).

The buildForm method, as you can see, is very similar to the methods we created. You may notice that we do not define the type of fields. This has happened because they were guessed from the entity field type.

To use this class, we need to slightly change our controller class, as follows:

<?php

namespace AppBundleController;

use AppBundleEntityTask;
use AppBundleFormTypeTaskType;
use SymfonyBundleFrameworkBundleControllerController;
use SymfonyComponentHttpFoundationRequest;

class TaskController extends Controller
{
    public function listAction(Request $request)
    {
        $em = $this->getDoctrine()->getManager();

        /../

        $newTask = new Task();

        $form = $this->createForm(new TaskType(), $newTask);

        return $this->render('task/list.html.twig', [
            'tasks' => $tasks,
            'form' => $form->createView()
        ]);
    }
}

In the preceding code, we replaced the code of the form with the newly created form type. All the other things remain the same, including displaying the types.

If you want to create a form type based on array data and not based on entity, you can, of course, do this simply by not defining a data class and passing an array of values instead of an entity object. In this case, you will have to define the type of the field.

Symfony2 has a lot of built-in types. The text fields can be defined as follows:

  • text, textarea: The standard HTML elements
  • email: The HTML5 input e-mail type
  • integer: The HTML5 input number type (used by the HTML5 browsers)
  • password: The standard HTML password field
  • money: This allows you to specify currency and provide options to customize input and output of data
  • number: This specializes in formatting numbers, allows to define precision, rounding, and so on
  • percent: This specializes in handling percentage data
  • search: The HTML5 input type search, supported by some browsers
  • url: This prepends submitted value with http://. if the protocol is not defined

There are also built-in types for choices (selects, radio buttons, and so on) and date/time handling:

  • choice: This renders the select/checkbox/radio button elements with given options
  • entity: This is a special choice field, designed to load options from an entity
  • language, country, timezone, currency: These allow the selection of language from a large list of languages, countries, time zones, or currencies
  • date, datetime, time, birthday: The various fields useful for handling the date and time (rendered as grouped select boxes)

There are also other more advanced field types, collections, repeated fields, and file fields, and you can also use one of the form type object to embed it into another form, so you can easily define your own subset of fields.

About the author

Deven Rathore

I'm Deven Rathore, a multidisciplinary & self-taught designer with 3 years of experience. I'm passionate about technology, music, coffee, traveling and everything visually stimulating. Constantly learning and experiencing new things.

Pin It on Pinterest

Shares

Get the best in web dev

Join dunebook.com and recieve best in web dev , once a week FREE

An email has been Sent to your Inbox ! Please Confirm your Subscription :)