Symfony
sponsored by SensioLabs
Menu
  • About
  • Documentation
  • Screencasts
  • Cloud
  • Certification
  • Community
  • Businesses
  • News
  • Download
  1. Home
  2. Documentation
  3. How to Create a Form Type Extension
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud
Search by Algolia

Table of Contents

  • Defining the Form Type Extension
  • Registering your Form Type Extension as a Service
  • Adding the extension Business Logic
  • Override the File Widget Template Fragment
  • Using the Form Type Extension
  • Generic Form Type Extensions

How to Create a Form Type Extension

Edit this page

Warning: You are browsing the documentation for Symfony 3.4, which is no longer maintained.

Read the updated version of this page for Symfony 6.2 (the current stable version).

How to Create a Form Type Extension

Form type extensions are incredibly powerful: they allow you to modify any existing form field types across the entire system.

They have 2 main use-cases:

  1. You want to add a specific feature to a single form type (such as adding a "download" feature to the FileType field type);
  2. You want to add a generic feature to several types (such as adding a "help" text to every "input text"-like type).

Imagine that you have a Media entity, and that each media is associated to a file. Your Media form uses a file type, but when editing the entity, you would like to see its image automatically rendered next to the file input.

Defining the Form Type Extension

First, create the form type extension class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/AppBundle/Form/Extension/ImageTypeExtension.php
namespace AppBundle\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FileType;

class ImageTypeExtension extends AbstractTypeExtension
{
    /**
     * Returns the name of the type being extended.
     *
     * @return string The name of the type being extended
     */
    public function getExtendedType()
    {
        // use FormType::class to modify (nearly) every field in the system
        return FileType::class;
    }
}

The only method you must implement is the getExtendedType() function. This is used to configure which field or field types you want to modify.

In addition to the getExtendedType() function, you will probably want to override one of the following methods:

  • buildForm()
  • buildView()
  • configureOptions()
  • finishView()

For more information on what those methods do, see the custom form field type article.

Registering your Form Type Extension as a Service

The next step is to make Symfony aware of your extension. Do this by registering your class as a service and using the form.type_extension tag:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
services:
    # ...

    AppBundle\Form\Extension\ImageTypeExtension:
        tags:
            - { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FileType }
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="AppBundle\Form\Extension\ImageTypeExtension">
            <tag name="form.type_extension" extended-type="Symfony\Component\Form\Extension\Core\Type\FileType"/>
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
use AppBundle\Form\Extension\ImageTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FileType;

$container->autowire(ImageTypeExtension::class)
    ->addTag('form.type_extension', [
        'extended_type' => FileType::class
    ])
;

The extended_type key of the tag must match the class you're returning from the getExtendedType() method. As soon as you do this, any method that you've overridden (e.g. buildForm()) will be called whenever any field of the given type (FileType) is built. Let's see an example next.

3.3

Prior to Symfony 3.3, you needed to define type extension services as public. Starting from Symfony 3.3, you can also define them as private.

Tip

There is an optional tag attribute called priority, which defaults to 0 and controls the order in which the form type extensions are loaded (the higher the priority, the earlier an extension is loaded). This is useful when you need to guarantee that one extension is loaded before or after another extension.

3.2

The priority attribute was introduced in Symfony 3.2.

Adding the extension Business Logic

The goal of your extension is to display a nice image next to file input (when the underlying model contains images). For that purpose, suppose that you use an approach similar to the one described in How to handle File Uploads with Doctrine: you have a Media model with a path property, corresponding to the image path in the database:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// src/AppBundle/Entity/Media.php
namespace AppBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;

class Media
{
    // ...

    /**
     * @var string The path - typically stored in the database
     */
    private $path;

    // ...

    public function getWebPath()
    {
        // ... $webPath being the full image URL, to be used in templates

        return $webPath;
    }
}

Your form type extension class will need to do two things in order to extend the FileType::class form type:

  1. Override the configureOptions() method so that any FileType field can have an image_property option;
  2. Override the buildView() methods to pass the image URL to the view.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// src/AppBundle/Form/Extension/ImageTypeExtension.php
namespace AppBundle\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PropertyAccess\PropertyAccess;

class ImageTypeExtension extends AbstractTypeExtension
{
    public function getExtendedType()
    {
        return FileType::class;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        // makes it legal for FileType fields to have an image_property option
        $resolver->setDefined(['image_property']);
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        if (isset($options['image_property'])) {
            // this will be whatever class/entity is bound to your form (e.g. Media)
            $parentData = $form->getParent()->getData();

            $imageUrl = null;
            if (null !== $parentData) {
                $accessor = PropertyAccess::createPropertyAccessor();
                $imageUrl = $accessor->getValue($parentData, $options['image_property']);
            }

            // sets an "image_url" variable that will be available when rendering this field
            $view->vars['image_url'] = $imageUrl;
        }
    }

}

Override the File Widget Template Fragment

Each field type is rendered by a template fragment. Those template fragments can be overridden in order to customize form rendering. For more information, you can refer to the How to Customize Form Rendering article.

In your extension class, you added a new variable (image_url), but you still need to take advantage of this new variable in your templates. Specifically, you need to override the file_widget block:

1
2
3
4
5
6
7
8
9
10
11
12
13
{# src/AppBundle/Resources/views/Form/fields.html.twig #}
{% extends 'form_div_layout.html.twig' %}

{% block file_widget %}
    {% spaceless %}

    {{ block('form_widget') }}
    {% if image_url is not null %}
        <img src="{{ asset(image_url) }}"/>
    {% endif %}

    {% endspaceless %}
{% endblock %}

Be sure to configure this form theme template so that the form system sees it.

Using the Form Type Extension

From now on, when adding a field of type FileType::class to your form, you can specify an image_property option that will be used to display an image next to the file field. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/AppBundle/Form/Type/MediaType.php
namespace AppBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

class MediaType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', TextType::class)
            ->add('file', FileType::class, ['image_property' => 'webPath']);
    }
}

When displaying the form, if the underlying model has already been associated with an image, you will see it displayed next to the file input.

Generic Form Type Extensions

You can modify several form types at once by specifying their common parent (Form Types Reference). For example, several form types inherit from the TextType form type (such as EmailType, SearchType, UrlType, etc.). A form type extension applying to TextType (i.e. whose getExtendedType() method returns TextType::class) would apply to all of these form types.

In the same way, since most form types natively available in Symfony inherit from the FormType form type, a form type extension applying to FormType would apply to all of these (notable exceptions are the ButtonType form types). Also keep in mind that if you created (or are using) a custom form type, it's possible that it does not extend FormType, and so your form type extension may not be applied to it.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
We stand with Ukraine.
Version:
Measure & Improve Symfony Code Performance

Measure & Improve Symfony Code Performance

Peruse our complete Symfony & PHP solutions catalog for your web development needs.

Peruse our complete Symfony & PHP solutions catalog for your web development needs.

↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

Avatar of Antonio Spinelli, a Symfony contributor

Thanks Antonio Spinelli for being a Symfony contributor

1 commit • 6 lines changed

View all contributors that help us make Symfony

Become a Symfony contributor

Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

Learn how to contribute

Symfony™ is a trademark of Symfony SAS. All rights reserved.

  • What is Symfony?
    • Symfony at a Glance
    • Symfony Components
    • Case Studies
    • Symfony Releases
    • Security Policy
    • Logo & Screenshots
    • Trademark & Licenses
    • symfony1 Legacy
  • Learn Symfony
    • Symfony Docs
    • Symfony Book
    • Reference
    • Bundles
    • Best Practices
    • Training
    • eLearning Platform
    • Certification
  • Screencasts
    • Learn Symfony
    • Learn PHP
    • Learn JavaScript
    • Learn Drupal
    • Learn RESTful APIs
  • Community
    • SymfonyConnect
    • Support
    • How to be Involved
    • Code of Conduct
    • Events & Meetups
    • Projects using Symfony
    • Downloads Stats
    • Contributors
    • Backers
  • Blog
    • Events & Meetups
    • A week of symfony
    • Case studies
    • Cloud
    • Community
    • Conferences
    • Diversity
    • Documentation
    • Living on the edge
    • Releases
    • Security Advisories
    • SymfonyInsight
    • Twig
    • SensioLabs
  • Services
    • SensioLabs services
    • Train developers
    • Manage your project quality
    • Improve your project performance
    • Host Symfony projects
    Deployed on
Follow Symfony
Search by Algolia