As you may know Symfony has extended support for forms, which goes from easily creating all kinds of form fields to validating them and eventually … formatting them.

Now the common approach to formatting a form, and the approach that symfony offers you by default when using doctrine:build-forms is to render each error, label and field separately. This has the advantage that you have full control over any customizing, but also has a few drawbacks:

  1. Help texts are not added by default and need to be manually inserted
  2. You’ll have a lot of code duplication that is not easily handled with partials
  3. You can much more easily get inconsistencies.
  4. You’ll need to manually add or remove any fields you add/delete, as well as customize them each time you change the form model.

Generalizing your forms

To avoid all this Symfony can also handle auto-generating the entire form for you.

Of course when you check this out you’ll notice that only the fields have been created, not even the <form&rt; tag or the submit button have been added. This will once again mean code repetition, so to prevent this I created a partial.

// /apps/frontend/layout/_form.php

renderFormTag($action); ?>


" />

Now we have successfully generalized our form we can include it from just about anywhere. Ex. on the login page.

<-- /apps/frontend/modules/user/templates/editSuccess.php -->
'form' => $form,
'action' => url_for('user_login'),
'submit' => __("Login")
)) ?>

Customizing your generalized forms

Now you probably wonder: but what about customizing? What about the designer? Or the frontend programmer?
No problem! First of all you can do a lot with CSS, but if that wasn’t enough, you can do any change on the form you require right in your template.

<-- /apps/frontend/modules/user/templates/editSuccess.php -->
$form->getWidgetSchema()->setLabel('pass', 'Password');
$form->getValidator('login')->setMessages(array(
'required' => "Please fill in your user name.",
'invalid' => "Invalid login. Please use only letters and _ or -.",
'notfound' => "No such user.",
'deleted' => "Your account is suspended."
));
$form->getWidget('pass')->setHelp('Forgot your password?');
include_partial('global/form', array(
'form' => $form,
'action' => url_for('user_login'),
'submit' => __("Login")
));

As a side-note you can notice I used translation only for the submit button – why? Because forms will automatically be translated. And to get their translation strings you can use the sfFormExtractorPlugin, which I wrote about in my I18n Message Extraction article.

You can change just about anything about your form with simple setters and it’s your own choice if you put the setters here, or put it in the configure() method of your custom forms. (I’d personally put them in the lib/form directory, but I can understand why a designer doesn’t want to work there).

The sfWidgetFormFormatter

Now if you use this method more extensively as I do you might come across some forms where you want to format just a tad different, or perhaps, like me, you want to change the general rules for form formatting.

For example you might want the ‘help’ instructions to appear above the field and the error, instead of below it and put them inside a div with a class so you can style them. Here’s how you’d do that:
// /lib/form/doctrine/BaseFormDoctrine.class.php
abstract class BaseFormDoctrine extends sfFormDoctrine // note that you can also do this in BaseForm or BaseFormPropel (the latter I never tried)
{
public function setup()
{
$help_format = "

%help%

";
$this->getWidgetSchema()->getFormFormatter()->setHelpFormat($help_format);

$row_format = "
n %label%

n %help%%error%%field%%hidden_fields%

n

n";
$this->getWidgetSchema()->getFormFormatter()->setRowFormat($row_format);
}
}

Web Forms UI Design

Maybe you want your form not to be a table. Here’s really interesting article on web form user interface design.. It basically says your eyes find it easier to go from top to bottom and not all the while going left-right-left-right-left-right as you do in a table.

Of course for that you’d need to change our global form:
// /apps/frontend/layout/_form.php

renderFormTag($action); ?>

" />

Is it me or did that form just became a ton simpler? That’s because Symfony automatically recognizes if it’s within a table and thus removes all table elements. Pretty nifty eh? But if you’d just process this you’d still have everything behind each other without organizing. Again you can use the formFormatter for this.
// /lib/form/doctrine/BaseFormDoctrine.class.php

abstract class BaseFormDoctrine extends sfFormDoctrine // note that you can also do this in BaseForm or BaseFormPropel (the latter I never tried)
{
public function setup()
{
$row_format = "

%label%
%error%%field%%help%%hidden_fields%

";
$this->getWidgetSchema()->getFormFormatter()->setRowFormat($row_format);
}
}

Everything will now be below each other and of course you can use CSS to put a margen on .field etc etc.

Symfony Tips & Tricks

  1. Symfony I18n and messages extraction
  2. Use gmail with swift mailer
  3. Generalizing and formatting forms