Symfony Forms Cookbook: Difference between revisions

From Littledamien Wiki
Jump to navigation Jump to search
No edit summary
Line 41: Line 41:
{# app/Resources/views/Default/new.html.twig #}
{# app/Resources/views/Default/new.html.twig #}
{{ form_start(form) }}
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
{{ form_end(form) }}
</syntaxhighlight>
</syntaxhighlight>
`form_end(form)` renders the form's end tag along with hidden fields, including CSRF protection. It also renders any fields that have not already been rendered.
=== Rendering form fields ===
* `form_row(form.property)` renders a form input along with a label and error messages specific to the form field. `form` is the name of the form variable, and `property` is the name of the property being rendered, e.g. `form.id`, `form.name`, etc.
* The components wrapped up in `form_row()` can be rendered separately:
** `form_label(form.property)` renders just the label for the field.
*** Override the label text with `form_label(form.property, 'Custom Label Text')`
** `form_errors(form.property)` renders errors for the field.
** `form_widget(form.property)` renders the form input.
*** Set properties for the input with `form_widget(form.property, {`attr`: {'class': 'my-custom-class'}})`
* Individual properties of the fields can be accessed with
** `form.entity.property.id`
** `form.entity.property.name`
** `form.entity.property.label`
** <span style="color:orange;">''^^ Actually I'm not 100% sure that's how it works. Try it out to confirm.''</span>


== Handling form submissions ==
== Handling form submissions ==

Revision as of 18:20, 5 February 2015

Basic form building

Build a form object and render it in a template from within a controller:[1]

// src/AppBundle/Controller/DefaultController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use AppBundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller
{
    public function newAction(Request $request)
    {
        // create a task and give it some dummy data for this example
        $task = new Task();
        $task->setTask('Write a blog post');
        $task->setDueDate(new \DateTime('tomorrow'));

        $form = $this->createFormBuilder($task)
            ->add('task', 'text')
            ->add('dueDate', 'date')
            ->add('save', 'submit', array('label' => 'Create Task'))
            ->getForm();

        return $this->render('Default/new.html.twig', array(
            'form' => $form->createView(),
        ));
    }
}

Render a form

After passing a form "view" object to a Twig template, the form can be rendered with form helper functions:[2]

{# app/Resources/views/Default/new.html.twig #}
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}

form_end(form) renders the form's end tag along with hidden fields, including CSRF protection. It also renders any fields that have not already been rendered.

Rendering form fields

  • form_row(form.property) renders a form input along with a label and error messages specific to the form field. form is the name of the form variable, and property is the name of the property being rendered, e.g. form.id, form.name, etc.
  • The components wrapped up in form_row() can be rendered separately:
    • form_label(form.property) renders just the label for the field.
      • Override the label text with form_label(form.property, 'Custom Label Text')
    • form_errors(form.property) renders errors for the field.
    • form_widget(form.property) renders the form input.
      • Set properties for the input with form_widget(form.property, {attr: {'class': 'my-custom-class'}})
  • Individual properties of the fields can be accessed with
    • form.entity.property.id
    • form.entity.property.name
    • form.entity.property.label
    • ^^ Actually I'm not 100% sure that's how it works. Try it out to confirm.

Handling form submissions

In the controller, the form object translates user data submitted with the form.[3]

public function newAction(Request $request)
{
    // just setup a fresh $task object (remove the dummy data)
    $task = new Task();

    $form = $this->createFormBuilder($task)
        ->add('task', 'text')
        ->add('dueDate', 'date')
        ->add('save', 'submit', array('label' => 'Create Task'))
        ->getForm();

    $form->handleRequest($request);

    if ($form->isValid()) {

        // perform some action, such as saving the task to the database

        return $this->redirect($this->generateUrl('task_success'));
    }

    // ...
}
  1. When the page is first loaded, the form is created and rendered.
    1. handleRequest() recognizes that the form was not submitted and does nothing.
    2. isValid() returns false if the form was not submitted.
  2. When the form is submitted, handleRequest() translates the data
    1. handleRequest() updates the corresponding properties of the entity object ($task in this case) with the form data.
    2. isValid() returns false if the form data isn't valid.
      1. Execution skips that block where the data would be processed, and instead goes to the original view, which is rendered with the submitted form data and error messages.

Multiple submit buttons

Add the buttons to the form builder in the controller:[4]

$form = $this->createFormBuilder($task)
    ->add('task', 'text')
    ->add('dueDate', 'date')
    ->add('save', 'submit', array('label' => 'Create Task'))
    ->add('saveAndAdd', 'submit', array('label' => 'Save and Add'))
    ->getForm();

Test which button was clicked in the controller using the button's isClicked() method:

if ($form->isValid()) {
    // ... perform some action, such as saving the task to the database

    $nextAction = $form->get('saveAndAdd')->isClicked()
        ? 'task_new'
        : 'task_success';

    return $this->redirect($this->generateUrl($nextAction));
}

Form validation

With Symfony, it isn't the form that is validated, rather it is the entity object that is tested to confirm that it contains valid data. This is does by defining a set of rules, or constraints, for the entity class.[5]

# AppBundle/Resources/config/validation.yml
AppBundle\Entity\Task:
    properties:
        task:
            - NotBlank: ~
        dueDate:
            - NotBlank: ~
            - Type: \DateTime

The above configuration specifies that the task field cannot be empty and the dueDate field cannot be empty and must be a valid DateTime object.

Validation groups

Different groups can be defined to validate the underlying object, either in the controller:

$form = $this->createFormBuilder($users, array(
    'validation_groups' => array('registration'),
))->add(...);

Or in a form class:

use Symfony\Component\OptionsResolver\OptionsResolverInterface;

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'validation_groups' => array('registration'),
    ));
}

Disabling validation

Set the validation_groups option to false:

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'validation_groups' => false,
    ));
}

Forms without classes

Example of how to set this up in a controller:[6]

// make sure you've imported the Request namespace above the class
use Symfony\Component\HttpFoundation\Request;
// ...

public function contactAction(Request $request)
{
    $defaultData = array('message' => 'Type your message here');
    $form = $this->createFormBuilder($defaultData)
        ->add('name', 'text')
        ->add('email', 'email')
        ->add('message', 'textarea')
        ->add('send', 'submit')
        ->getForm();

    $form->handleRequest($request);

    if ($form->isValid()) {
        // data is an array with "name", "email", and "message" keys
        $data = $form->getData();
    }

    // ... render the form
}

Notes

  1. Building The Form, Symfony forms documentation
  2. Rendering The Form, Symfony forms documentation
  3. Handling Form Submissions, Symfony forms documentation
  4. Submitting Forms With Multiple Buttons, Symfony forms documentation
  5. Form Validation, Symfony forms documentation
  6. Using a Form Without a Class, Symfony forms documentation