Skip to content
Bram Gotink edited this page Jul 5, 2014 · 1 revision
This page applies to branch dev-form

This page talks about the new form structure, how to create forms and how to use them.

Form

A form corresponds to an html <form></form> entity. They are used to request data from the user. This data is often stored in entities, but can also be used to e.g. log in or send a mail.

Admin vs. site

The admin doesn't use bootstrap, but the front-end website does. Because other classes have to be added to elements in these two cases, a different form class is used for each case:

CommonBundle\Component\Form\Admin\Form
CommonBundle\Component\Form\Site\Form

Creating a form

To create a form, create a new class extending the right Form superclass:

class MyForm extends \CommonBundle\Component\Form\Admin\Form
{
    // more info in subsections
}

A form is initialised in its init() method, it provides an input filter and it can use a hydrator to directly hydrate the entered data into an object.

initialising the form

The init() function is called automatically. Do not use the constructor! The code you write in the init function has access to the ServiceLocator, so you can easily access the entity manager, the document manager, the translator, the current academic year etc.

New elements are added using $this->add(...);. You could create the element instances and add these, but it is better to let the form create the instances:

$this->add(array(
    'type'       => 'text', // standard text input
    'name'       => 'my_element', // allows you to access the element using $this->get('my_element')
    'label'      => 'My Element', // the HTML label for the element, entirely optional
    'required'   => true, // set the element to required, defaults to false if not set
    'value'      => '', // suppose you'd want to set the value
    'attributes' => array(
        // you can set HTML attributes for the element here, e.g.:
        'placeholder' => 'Enter text here!',
        'disabled'    => true,
    ),
    'options'    => array(
        'input' => array(
            // see the section on validation
        ),
    ),
));

Because a submit element is almost always the same, apart from the content of the button and the HTML classes, we've created a helper function:

$this->addSubmit('Button Text', 'button_classes', 'element_name');

All parameters but the first are optional. The last parameter gives the name of the element in the form.

Input validation

See below, this is too long for a subsubsection.

Hydrating

The hydrator to use can be defined by setting $hydrator. This has to happen before the constructor is called, so do it like this:

class MyForm extends ...
{
    protected $hydrator = 'MyBundle\Hydrator\Path\To\Hydrator';
    // ...
}

For more information, see the wiki page about Hydrators.

Input validation

Form input should be validated, and can be filtered as well. A useful filter for text inputs is the StringTrim, to remove all leading and trailing whitespace from the entered data.

The getInputFilterSpecification method provides the specification of the input filters. The form superclass provides a great implementation though. It collects the specification of all the elements in the form, and combines these into one specification. Sometimes you'll have to override this behaviour though. An example is when there's a checkbox in the form that changes the required attribute of some elements.

The default way

This example adds the StringTrim filter, and checks whether the string is a valid hostname:

$this->add(array(
    // ...
    'options'    => array(
        'input' => array(
            'filters'  => array(
                array('name' => 'StringTrim'),
            ),
            'validators' => array(
                array(
                    'name' => 'Hostname',
                    'options' => array(
                         'allow' => HostnameValidator::ALLOW_ALL
                    )
                ),
            ),
        ),
    ),
));

There are three ways to set an element as "required". Each way has a different effect though:

  • Set the HTML attribute. This will show the user that the element is required, even though the form doesn't care whether it's entered or not.
$this->add(array(
    // ...
    'attributes' => array(
        'required' => true|false,
    ),
));
- Set the required attribute in the input filter. This will not show the user that the element is required, but will result in an invalid form if the element is empty.
```php
$this->add(array(
    // ...
    'options' => array(
        'input' => array(
            'required' => true|false,
        ),
    ),
));
  • The default way, this will show the user that the element is required, and the form will complain if the element is empty.
$this->add(array(
    // ...
    'required' => true|false,
));

You can use the three methods simultaneously. The first two methods will override the behaviour of the third. You can e.g. make an element required without showing the user using

$this->add(array(
    // ...
    'options' => array(
        'input' => array(
            'required' => true,
        ),
    ),
));

or using

$this->add(array(
    // ...
    'required' => true,
    'attributes' => array(
        'required' => false,
    ),
));

The hard way

Please ensure that the structure of the specification you return has the same structure as the form. Example:

public function getInputFilterSpecification()
{
    return array(
        'my_element' => array(
            'name' => 'my_element',
            'required' => ...,
            // ...
        ),
    );
}

Accessing a form

You can not create a form using the constructor. This will not perform the right initialisation (e.g. injecting the service locator, calling init()). Instead, you'll have to go through the FormFactory. Instances can be obtained form the service locator, but Controllers have a specific method to get a form:

$this->getForm('name_of_form');

The name of the form is as follows:

MyBundle\Form\Path\To\Form -> my_path_to_form
MyBundle\Form\Admin\Path\To\Form -> my_path_to_form, but requires an admin controller

Binding an object in the form and injecting parameters

There are three ways to give an object to the form:

  • Simply create a setter and use it. Know that this setter will be called after the init() method call!
  • By binding an object to the form. You can do this using
$form = $this->getForm('...');
$form->bind($object);

or using

$form = $this->getForm('...', $object);

Just like the previous method, the object will be bound after init().

  • By calling getForm like this:
$form = $this->getForm('...', array('my_entity' => $object));

This will call $form->setMyEntity($object). Note that this call will happen before init() has run. The objects will not be bound to the form, so they won't be updated automatically when data is set in the form. You can call bind in the controller if you like, or you could call bind at the end of init().