Improving the Forms: Layouts and Formatters

Keeping all parts of a website consistent is one of the most time consuming tasks of a web designer. Spending this time is worth doing, because the more consistent the look and feel of your website is, the more professional it appears to its users.

I will show you today how symfony could help you keeping all of your forms consistent with very little work left for you to do.

The concept presented in this article series is not implemented in symfony. It has been developed together with a few other people in the community. With these articles, I would like to receive feedback about how you like the concept and how it can be improved.

  1. Improving the Forms
  2. Improving the Forms: Field Groups
  3. Improving the Forms: Layouts and Formatters

A Quick Look Back

In my last post I introduced you to the concept of field groups. Field groups are similar to embedded forms in that they group a set of form fields together and enable combined data binding and validation. They differ from embedded forms in that they can combine multiple fields to enter one single value. I showed you the example of a password field with two fields, that is used to enter one single value: The password.

Here is the code for the user profile form that we have developed in the course of this article series:

// lib/form/doctrine/ProfileForm.class.php
class ProfileForm extends sfFormDoctrine
  protected $user = null;

  public function __construct(sfUser $user)
    $this->user = $user;

  public function configure()
    $this->addField('name', new sfTextField(15))
         ->setHelp('Your full name, f.i. "John Travolta"');
    $this->addField('email', new sfEmailField())
         ->setHelp('A valid email address, f.i. ""');
    $this->addField('password', new VerifiedPasswordField())
         ->setHelp('Must not be "pulpfiction"');

      new sfValidatorCallback($this, 'comparePasswordAndName')

    if (!$this->user->hasCredential('admin'))

  public function comparePasswordAndName(sfValidator $validator, array $values)
    if ($values['name'] == $values['password'])
      throw new sfValidatorError('The password must not be the same as the name',

I invite you to compare this form with the original symfony implementation of the same functionality shown in my first post.

Form Layouts

Today I want to present you another advantage of the field groups. You can use "layouts" on them to automate the generation of HTML code in a very flexible way.

Right now, symfony offers two alternatives to create the HTML code of your forms:

  1. Use one of the built-in form formatters "list" and "table". These formatters are very unflexible because the whole form will be rendered in the same way. You cannot modify the formatters to render some parts as fieldsets, others as multi-column fields etc.

  2. Code the HTML by hand in your templates. This way gives the most power to web designers. Unfortunately it also leads to a lot of code duplication in your templates that cannot be easily resolved by using partials. Maintaining this duplicate code is quite repetitive, tedious, and error prone™ ;-)

Layouts help you to solve this problem. But what exactly are layouts?

In general, a layout is a set of different classes (the "formatters") that specify how different parts of a form should be rendered. Look at the following sketch to get an idea of what layouts can do for you:

Examples of layouts

Usually, layouts render the individual fields in rows with the label to the left and the field to the right. You can change individual fields or field groups to be rendered in a different way: As fieldset (useful for field groups), with the label to the right (useful for checkboxes), with no label at all or with your very own custom formatter.

But layouts can do much more than formatting the individual rows: They also know how the formatted fields should be visually organized. You can, for example, display all fields in a horizontal row or organize them in a grid with multiple columns.

Each field group (the form is also a field group) has exactly one layout assigned that is used to organize the fields in this group. Because field groups can be nested within each other, you can also nest different layouts. Let's look at a basic example that can be achieved very easily:

Profile form with layouts

The password field and the permissions field group are configured to be formatted as fieldset. The permissions field group itself has a multi-column layout applied. Later in this post I will show you how little PHP code is necessary to achieve this result.

Apart from the visual aspect, layouts specify which HTML code will be used to render the form. sfListLayout renders the form in an ul tag, sfTableLayout renders the form in a … guess what … right, in a <table> tag. Of course you can also implement your own layout that renders the whole form in a combination of <address> and <blockquote> tags, if that makes sense to you (tag misuse at its best!).

Multi-Column Layouts

Layouts with multiple columns are especially interesting. They help you to display a lot of information in little, well-organized space.

If you use <ul>-tags to render your form, a multi-column layout can be achieved very easily. You just need to change the CSS of the <li>-tags to make them floating. By giving them a fixed width, you can control how many form fields are displayed in a row.

There is a big problem with this solution though: Fields are first rendered from left to right, then from top to bottom.

This can be a problem if your fields are sorted in a specific order. A good example are large lists of checkboxes. If the labels of these fields are sorted alphabetically, users expect them to be sorted from top to bottom, then from left to right.

Layouts with multiple columns

Layouts are able to deal with this problem easily. You can configure the number of columns and the order of the fields ("horizontal" or "vertical") simply by passing these options to the layout's constructor.

Show Me the Code

Configuring layouts in your form is easy. Every class inheriting from sfFormFieldGroup (thus also sfForm) offers the method setLayout(). To this method you can pass an instance of the layout you want to use.

public function configure()
  $this->setLayout(new sfListLayout());

The different layout classes allow different options that you can pass to their constructor. Two such options are the number of columns and the order in which the fields are put into these columns.

public function configure()
  $this->setLayout(new sfListLayout(array(
    'columns' => 3,
    'order'   => sfLayout::VERTICAL,

With as little code as this you have configured your form to organize all fields in 3 columns in a vertical order using ul and li-tags. You can achieve the same result using an HTML table by passing an instance of sfTableLayout instead of sfListLayout.

The format of the individual form fields can be modified by calling the method setFormat() on the field. To this method you pass the name of the format you want to use. The names of the different formats are declared in the layout class, as you will see later.

public function configure() { $this->addField('password', new VerifiedPasswordField()) ->setFormat('fieldset'); } After this simple definition, the password fields are rendered as in the picture above. Depending on the layout, there can be many more formats like "leftlabel" (the default), "nolabel" or "rightlabel". Look at the first sketch in this post to see what these look like.

A More Complex Example

Now that we have learned about the basics, we can look at how to configure our user profile form to be displayed like in the above picture.

First, we tell our form to be rendered as HTML list.

public function configure()
  $this->setLayout(new sfListLayout());

Now we add the fields as we did before. Note the format that we have set on the password field to render it as a fieldset.

  $this->addField('name', new sfTextField(15))
       ->setHelp('Your full name, f.i. "John Travolta"');
  $this->addField('email', new sfEmailField())
       ->setHelp('A valid email address, f.i. ""');
  $this->addField('password', new VerifiedPasswordField())
       ->setHelp('Must not be "pulpfiction"')

Just for fun, we'll add a few checkboxes to our form to demonstrate how a multi-grid layout is integrated.

  $permissions = array(
    'admin', 'blog-editor', 'content-editor',
    'publisher', 'file-editor', 'comment-editor',
  $this->addField('permissions', new sfCheckboxList($permissions))
       ->setHelp('Choose one or more permissions for this user')
       ->setLayout(new sfListLayout(array('columns' => 3));

The last thing left is to declare the final validator and the user credential check again, but this code has not changed.

Let's sum up what we have learned:

  1. A format is the definition of how a single field is rendered
  2. A layout is the definition of how a group of fields is organized

But What About the Web Designers?

I can hear voices screaming that I am violating the MVC separation with this proposal. But honestly, I am not. :-) We still have one specific component that is responsible for generating the output of our form: The sfLayout instance. You can simply modify the way your form is rendered by exchanging its layout, which is what MVC is all about.

Many people misunderstand the MVC concept as it is applied in symfony. Although the view layer consists mostly of templates, it is not restricted to that. MVC simply requires you to program separate decoupled layers that can be easily exchanged. Whether you put the code of these layers in templates, classes or coke cans does not matter.

The good news is that you can do all the configuration from within your templates, if you don't like to do it in your configure() method. All of the above methods are accessible using sfForm's array access syntax. Together with a set of easy convenience methods, even web designers could configure the form's format.

<?php $form->setLayout('list') ?>
<?php $form['password']->setFormat('fieldset') ?>
<?php $form['permissions']->setFormat('fieldset') ?>
<?php $form['permissions']->setLayout('list', array('columns' => 2)) ?>

<?php echo $form ?>

For small projects with one or only very few forms, I recommend you to not use layouts and formatters, if that eases the life of your web designers. You can still output the form the old-fashioned way and write all the HTML by hand.

sfLayout Internals

In the last part of our travel to the magic layout wonderland, I want to explain some of the internals of sfLayout to you.

The information in this section is only relevant when you create your own layout. You hardly ever need this information to simply configure your form.

All layouts must implement sfLayoutInterface.

interface sfLayoutInterface
  public function createFieldFormatter($format = null);
  public function createFieldGroupFormatter();

Layouts follow the Abstract Factory Pattern. That means that any class implementing sfLayoutInterface simply creates instances of other classes that are connected in some way.

Let's visualize this very technical definition with a simple example: sfListLayout is able to create new sfFieldFormatter instances as well as sfFieldGroupFormatter instances. Because both instances must know that the form is rendered as <ul>-list, they structurally belong together. If you use a different layout, you get a different set of formatters that again know about each other. That's what this pattern is about.

It is the responsibility of sfFieldFormatter and sfFieldGroupFormatter instances to turn the fields/field groups into HTML code.

sfLayoutInterface::createFieldFormatter() has one single parameter "format". This format is exactly the format string that you have set on the form fields earlier using setFormat(). For each given format, the layout knows which object to return.

$layout = new sfListLayout();
$formatter = $layout->createFieldFormatter('rightlabel');
echo get_class($formatter);

// returns "sfRightLabelListFieldFormatter"

The great thing is that you never need to know about these long class names, because the layout you use hides them from you. All returned objects share the same interface: sfFieldFormatterInterface. You only ever need to know about this interface and never about the concrete class you receive.

interface sfFieldFormatterInterface
  public function render($id, $label, $value, $help, array $attributes = array());


Enough theory for today. I hope I was able to give you an idea of what power layouts can give to you. As you have seen, they can reduce code duplication in large applications with many forms and simplify the presentation of forms. There is much more to tell about layouts, but this post has grown long enough.

How do you like the layouts? Is this idea worth the time following?