Rich Sage

> home

Adding non-entity fields to your Symfony2 forms

july 2011

UPDATE: The example below is for Symfony 2.0. For Symfony 2.1 onwards, you should use the "mapped" option instead of "property_path".

UPDATE #2: Bernhard Schussek has provided a simplified version for Symfony 2.1 onwards - thanks Bernhard:

<?php
use Symfony\Component\Validator\Constraints\True;

// in your buildForm() method:
$builder
    ->add("firstName", "text")
    ->add("lastName", "text")
    ->add("emailAddress", "email")
    ->add("t_and_c", "checkbox", array(
        "mapped" => false,
        "constraints" => new True(array(
            "message" => "Please accept the Terms and conditions in order to register")
        ),
    )
);

I’ve had a case recently whereby I needed to implement a registration form in my current Symfony2 application, based on a Mandango model. This form consisted of selected fields from my model, along with a separate “I accept the terms and conditions” checkbox; pretty standard. In Symfony 1, it was straightforward to add in non-model fields, however in Symfony2 the convention for forms is to create a domain model which represents your form, which then gets populated with the data.

Initially I went with extending from the Mandango model (in my case, Model\MyOwnBundle\User being the original model) and adding in a separate get/setTermsAndConditions() method, but this caused problems when saving the model down, as Mandango couldn’t find the document class (since I’d extended it and called it UserRegistration).

After a bit of digging and experimentation however, the following code does what I need it to, without having to extend my existing model or create a new one (which I’d then have to use to populate my actual Mandango model):

<?php
use Symfony\Component\Form\AbstractType,
    Symfony\Component\Form\CallbackValidator,
    Symfony\Component\Form\FormBuilder,
    Symfony\Component\Form\FormError,
    Symfony\Component\Form\FormInterface;

class RegisterFormType extends AbstractType
{
    /**
     * Constructs our registration form
     *
     * @param \Symfony\Component\Form\FormBuilder $builder
     * @param array $options
     * @return void
     */
    public function buildForm(FormBuilder $builder, array $options)
    {
        // Build the form
        $builder->
            add("firstName", "text")->
            add("lastName", "text")->
            add("emailAddress", "email")->
            add("t_and_c",
                "checkbox",
                array(
                    "property_path" => false,
                )
            );

        // "True" validator on the form for t&c
        $builder->
            addValidator(new CallbackValidator(function(FormInterface $form)
            {
                if (!$form["t_and_c"]->getData())
                {
                    $form->addError(new FormError('Please accept the terms and conditions in order to register'));
                }
            })
        );
    }

    // ...
}

The above adds the t_and_c field but using "property_path" => false means it’s not validated against the model and therefore no need to implement separate get/set() methods. Instead we use a callback validator on the form to check the value of the t_and_c field and ensure it’s been checked. Et voila :-)


comments powered by Disqus
Rich Sage

Hello, I'm Rich :-)