I see this is an older thread, but I wanted to offer an alternate solution in case others find it useful. I was unsatisfied with other answers on this topic, because I wanted to do three things:
- Not have to do any custom form rendering in each view template (I like to just use
{{ form(form) }})
- Have the cancel button appear right next to the submit button (surprisingly tricky, since symfony by default wraps all form buttons in a div tag if you don't do custom rendering)
- Be able to set the cancel action as an option in the controller, since the link will vary depending on whether I'm adding or editing, etc
And I wanted to do all this while applying the DRY principle. To start, I extended the base form type to allow a custom option, 'cancel_action':
<?php
namespace AcmeBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FormTypeExtension extends AbstractTypeExtension
{
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver->setDefined('cancel_action');
$resolver->setAllowedTypes('cancel_action', 'string');
}
/**
* {@inheritdoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
parent::buildView($view, $form, $options);
if (isset($options['cancel_action'])) {
$view->vars['cancel_action'] = $options['cancel_action'];
}
}
/**
* {@inheritdoc}
*/
public function getExtendedType()
{
return 'form';
}
}
And registered that form type extension in my config (using alias 'form' so that it would be available for all my forms):
acme.form_type_extension:
class: AcmeBundle\Form\Extension\FormTypeExtension
tags:
- {name: form.type_extension, alias: form}
Then I used form theming to extend the base form view (I extended the submit_widget block so that the cancel button would be next to the submit button inside the div tag):
{# AcmeBundle:Form:fields.html.twig #}
{% extends 'form_div_layout.html.twig' %}
{%- block submit_widget -%}
{%- set type = type|default('submit') -%}
{{ block('button_widget') }}
{% if form.parent.vars.cancel_action is defined %}
<a class="btn btn-default" href="{{ form.parent.vars.cancel_action }}" role="button">Cancel</a>
{% endif %}
{%- endblock submit_widget -%}
And registered that form theme in my twig config so that it would apply by default to all form views:
twig:
form_themes:
- 'AcmeBundle:Form:fields.html.twig'
Et voila! Now for each form, if I want a cancel button, all I need to do is specify the cancel action as an option when I initialize the form in the controller:
$form = $this->createForm(new AlbumType(), $album, array(
'cancel_action' => $this->generateUrl('album_home')
));