The form builder in Symfony 2 is very flexible and has dozens of field options, but there will inevitably come a time when you need to create your own custom form field. The Symfony Cookbook has a great article on how to create a custom form field type and use it in your project.

 

As an example, let’s create an input HTML5 element that is not part of the form builder, the tel input type, which should be rendered like this:

<input type="tel" id="form_name_phone" name="form_name[phone]">

 

Create the Form Field Type Class

// Acme/Bundle/Form/Type

namespace Acme\Bundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class TelType extends AbstractType
{
    /**
     * @author  Joe Sexton <joe@webtipblog.com>
     * @return  string
     */
    public function getName()
    {
        return 'tel';
    }

    /**
     * @author  Joe Sexton <joe@webtipblog.com>
     * @return  string
     */
    public function getParent()
    {
        return 'text';
    }
}

getName() returns the name of the form field type and getParent() defines the parent element as text so we can use many of the text input options.

 

Create the Form Field Template

Since the tel input is very similar to the text input, we’ll re-use the text element’s template but just change the type=”text” to type=”tel”. The input element is defined in Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig within the form_widget_simple block.

{# Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig #}

{% block form_widget_simple %}
{% spaceless %}
    {% set type = type|default('text') %}
    <input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endspaceless %}
{% endblock form_widget_simple %}

 

To create the new tel_widget, start by creating a new fields.html.twig file your bundle’s Resources/views/Form directory. The twig tel_widget definition should be defined in the fields.html.twig file and should look like this:

{# AcmeBundle/Resources/views/Form/fields.html.twig #}

{% block tel_widget %}
    {% spaceless %}
        {% set type = 'tel' %}
        {{ block('form_widget_simple') }}

    {% endspaceless %}
{% endblock %}

Note the name of the widget is tel_widget, this is important! The name of the Twig block should be the form field type’s name with an _widget in order to be rendered. Also note that this block just sets the type variable to tel and then calls the form_widget_simple block, which just reuses the input element that already exists. This can be done with checkboxes, radio buttons, etc.

 

Configuration

Now, simply update the app/config.yml file to use the new fields.html.twig file when rendering forms.

# app/config/config.yml

twig:
    form:
        resources:
            - 'AcmeBundle:Form:fields.html.twig'

 

Use the Form Field Type

To use the new form field type, add the field to another form type like this:

// src/Acme/Bundle/Form/Type/TestType.php

namespace Acme\Bundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Acme\Bundle\Form\Type\TelType;

class TestType extends AbstractType
{
    /**
     * @author  Joe Sexton <joe@webtipblog.com>
     * @param  	FormBuilderInterface $builder
     * @param  	array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('phone', new TelType(), array(
            'label' => 'Phone Number',
        ));
    }
}

 

Create a Service and Alias

You can also create a service for the form type along with an alias so the type can be used without manually instantiating the TelType class like all of the other built-in form types. To do so, edit your bundle’s services.yml file. You must make the alias the same name as the form type’s getName() method return value.

# src/Acme/Bundle/Resources/config/services.yml

services:
    acme.form.type.tel:
        class: Acme\Bundle\Form\Type\TelType
        tags:
            - { name: form.type, alias: tel }

 

After you create a service and alias, you can use the tel type in another form type like this:

// src/Acme/Bundle/Form/Type/TestType.php

namespace Acme\Bundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Acme\Bundle\Form\Type\TelType;

class TestType extends AbstractType
{
    /**
     * @author  Joe Sexton <joe@webtipblog.com>
     * @param  	FormBuilderInterface $builder
     * @param  	array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('phone', 'tel', array(
            'label' => 'Phone Number',
        ));
    }
}

 

That’s all there is to it!

6 comments on “Create a Custom Form Field Type in Symfony 2

  1. alireza says:

    Great great explanation. Thanks.

  2. Orange says:

    Hey, what is the best and easy way to inject some javascript before when using a custom type like this?

  3. Osoian Marcel says:

    Thank you!

  4. george says:

    There is an easier way to set the type without having to create a custom template. In the TelType class, simply add a buildView function that looks like this:

    vars[‘type’] = ‘tel’;
    }

    ?>

    As explained here: http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html, the buildView function sets the variables that are used in the template. Since TelType is “extending” the TextType, it uses the same template. And if you take a look at the default template in the post above, you’ll notice that one of the variables checked for is “type”, with a default set to ‘text’. So just set the “type” variable, and you’re off to the races!

    Take a look at the ChoiceType for a more complex example:
    https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php

    1. george says:

      Huh, the comment parsing engine stripped out my code. Let’s see if I can get some formatting in using pre:

      /* ... */
      use Symfony\Component\Form\FormInterface;
      use Symfony\Component\Form\FormView;
      /* ... */
      
          public function buildView(FormView $view, FormInterface $form, array $options)
          {
              $view->vars['type'] = 'tel';
          }
      /* ... */
      
  5. Mohamed Essam says:

    Thanks very much it was very helpful to me

Comments are closed.