` to
+add this new template at the beginning of the list (the first one overrides the
+rest of files):
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/twig.yaml
+ twig:
+ form_themes:
+ - 'form/custom_types.html.twig'
+ - '...'
+
+ .. code-block:: xml
+
+
+
+
+
+
+ form/custom_types.html.twig
+ ...
+
+
+
+ .. code-block:: php
+
+ // config/packages/twig.php
+ $container->loadFromExtension('twig', [
+ 'form_themes' => [
+ 'form/custom_types.html.twig',
+ '...',
+ ],
+ ]);
+
+The last step is to create the actual Twig template that will render the type.
+The template contents depend on which HTML, CSS and JavaScript frameworks and
+libraries are used in your application:
+
+.. code-block:: twig
+
+ {# templates/form/custom_types.html.twig #}
+ {% block postal_address_row %}
+ {% for child in form.children if not child.rendered %}
+
+ {{ form_label(child) }}
+ {{ form_widget(child) }}
+ {{ form_help(child) }}
+ {{ form_errors(child) }}
+
+ {% endfor %}
+ {% endblock %}
-.. _form-field-service:
-.. _creating-your-field-type-as-a-service:
+.. note::
-Accessing Services and Config
------------------------------
+ Symfony 4.2 deprecated calling ``FormRenderer::searchAndRenderBlock`` for
+ fields that have already been rendered. That's why the previous example
+ includes the ``... if not child.rendered`` statement.
-If you need to access :doc:`services ` from your form class,
-add a ``__construct()`` method like normal::
+The first part of the Twig block name (e.g. ``postal_address``) comes from the
+class name (``PostalAddressType`` -> ``postal_address``). This can be controlled
+by overriding the ``getBlockPrefix()`` method in ``PostalAddressType``. The
+second part of the Twig block name (e.g. ``_row``) defines which form type part
+is being rendered (row, widget, help, errors, etc.)
- // src/Form/Type/ShippingType.php
- namespace App\Form\Type;
+The article about form themes explains the
+:ref:`form fragment naming rules ` in detail. The
+following diagram shows some of the Twig block names defined in this example:
- // ...
+.. raw:: html
+
+
+
+.. caution::
+
+ When the name of your form class matches any of the built-in field types,
+ your form might not be rendered correctly. A form type named
+ ``App\Form\PasswordType`` will have the same block name as the built-in
+ ``PasswordType`` and won't be rendered correctly. Override the
+ ``getBlockPrefix()`` method to return a unique block prefix (e.g.
+ ``app_password``) to avoid collisions.
+
+Passing Variables to the Form Type Template
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Symfony passes a series of variables to the template used to render the form
+type. You can also pass your own variables, which can be based on the options
+defined by the form or be completely independent::
+
+
+ // src/Form/Type/PostalAddressType.php
use Doctrine\ORM\EntityManagerInterface;
+ // ...
- class ShippingType extends AbstractType
+ class PostalAddressType extends AbstractType
{
private $entityManager;
@@ -312,19 +465,39 @@ add a ``__construct()`` method like normal::
$this->entityManager = $entityManager;
}
- // use $this->entityManager down anywhere you want ...
+ // ...
+
+ public function buildView(FormView $view, FormInterface $form, array $options)
+ {
+ // pass the form type option directly to the template
+ $view->vars['isExtendedAddress'] = $options['is_extended_address'];
+
+ // make a database query to find possible notifications related to postal addresses (e.g. to
+ // display dynamic messages such as 'Delivery to XX and YY states will be added next week!')
+ $view->vars['notification'] = $this->entityManager->find('...');
+ }
}
-If you're using the default ``services.yaml`` configuration (i.e. services from the
-``Form/`` are loaded and ``autoconfigure`` is enabled), this will already work!
-See :ref:`service-container-creating-service` for more details.
+If you're using the :ref:`default services.yaml configuration `,
+this example will already work! Otherwise, :ref:`create a service `
+for this form class and :doc:`tag it ` with ``form.type``.
-.. tip::
+The variables added in ``buildView()`` are available in the form type template
+as any other regular Twig variable:
- If you're not using :ref:`autoconfigure `, make sure
- to :doc:`tag ` your service with ``form.type``.
+.. code-block:: twig
-Have fun!
+ {# templates/form/custom_types.html.twig #}
+ {% block postal_address_row %}
+ {# ... #}
-.. _`ChoiceType`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
-.. _`FieldType`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php
+ {% if isExtendedAddress %}
+ {# ... #}
+ {% endif %}
+
+ {% if notification is not empty %}
+
+ {{ notification }}
+
+ {% endif %}
+ {% endblock %}
diff --git a/form/create_form_type_extension.rst b/form/create_form_type_extension.rst
index aaff319711a..fb1cd2e02b2 100644
--- a/form/create_form_type_extension.rst
+++ b/form/create_form_type_extension.rst
@@ -60,8 +60,9 @@ For more information on what those methods do, see the
Registering your Form Type Extension as a Service
-------------------------------------------------
-Form type extensions must be registered as services and :doc:`tagged `
-with the ``form.type_extension`` tag. If you're using the
+Form type extensions must be :ref:`registered as services `
+and :doc:`tagged ` with the ``form.type_extension`` tag.
+If you're using the
:ref:`default services.yaml configuration `,
this is already done for you, thanks to :ref:`autoconfiguration `.
diff --git a/form/data_transformers.rst b/form/data_transformers.rst
index 8775744bdb7..8692c2a0ddf 100644
--- a/form/data_transformers.rst
+++ b/form/data_transformers.rst
@@ -291,14 +291,13 @@ and type-hint the new class::
// ...
}
-That's it! As long as you're using :ref:`autowire ` and
-:ref:`autoconfigure `, Symfony will automatically
-know to pass your ``TaskType`` an instance of the ``IssueToNumberTransformer``.
-
-.. tip::
-
- For more information about defining form types as services, read
- :doc:`register your form type as a service `.
+That's it! If you're using the
+:ref:`default services.yaml configuration `,
+Symfony will automatically know to pass your ``TaskType`` an instance of the
+``IssueToNumberTransformer`` thanks to :ref:`autowire ` and
+:ref:`autoconfigure `.
+Otherwise, :ref:`register the form class as a service `
+and :doc:`tag it ` with the ``form.type`` tag.
Now, you can use your ``TaskType``::
diff --git a/form/dynamic_form_modification.rst b/form/dynamic_form_modification.rst
index 31bb508fb15..b101e43f193 100644
--- a/form/dynamic_form_modification.rst
+++ b/form/dynamic_form_modification.rst
@@ -326,10 +326,11 @@ security helper to fill in the listener logic::
Using the Form
~~~~~~~~~~~~~~
-If you're using :ref:`autowire ` and
-:ref:`autoconfigure `, your form is ready to be used!
-Otherwise, see :doc:`/form/form_dependencies` to learn how to register your form
-type as a service.
+If you're using the :ref:`default services.yaml configuration `,
+your form is ready to be used thanks to :ref:`autowire ` and
+:ref:`autoconfigure `.
+Otherwise, :ref:`register the form class as a service `
+and :doc:`tag it ` with the ``form.type`` tag.
In a controller, create the form like normal::
diff --git a/form/form_dependencies.rst b/form/form_dependencies.rst
index 186a13a5916..96b067362ff 100644
--- a/form/form_dependencies.rst
+++ b/form/form_dependencies.rst
@@ -1,144 +1,12 @@
How to Access Services or Config from Inside a Form
===================================================
-Sometimes, you may need to access a :doc:`service ` or other
-configuration from inside of your form class. To do this, you have 2 options:
-
-1) Pass Options to your Form
-----------------------------
-
-The simplest way to pass services or configuration to your form is via form *options*.
-Suppose you need to access the Doctrine entity manager so that you can make a
-query. First, allow (in fact, require) a new ``entity_manager`` option to be
-passed to your form::
-
- // src/Form/TaskType.php
- // ...
-
- class TaskType extends AbstractType
- {
- // ...
-
- public function configureOptions(OptionsResolver $resolver)
- {
- // ...
-
- $resolver->setRequired('entity_manager');
- }
- }
-
-Now that you've done this, you *must* pass an ``entity_manager`` option when you
-create your form::
-
- // src/Controller/DefaultController.php
- use App\Form\TaskType;
-
- // ...
- public function new()
- {
- $entityManager = $this->getDoctrine()->getManager();
-
- $task = ...;
- $form = $this->createForm(TaskType::class, $task, [
- 'entity_manager' => $entityManager,
- ]);
-
- // ...
- }
-
-Finally, the ``entity_manager`` option is accessible in the ``$options`` argument
-of your ``buildForm()`` method::
-
- // src/Form/TaskType.php
- // ...
-
- class TaskType extends AbstractType
- {
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- /** @var \Doctrine\ORM\EntityManager $entityManager */
- $entityManager = $options['entity_manager'];
- // ...
- }
-
- // ...
- }
-
-Use this method to pass *anything* to your form.
-
-2) Define your Form as a Service
---------------------------------
-
-Alternatively, you can define your form class as a service. This is a good idea if
-you want to re-use the form in several places - registering it as a service makes
-this easier.
-
-Suppose you need to access the :ref:`EntityManager ` object
-so that you can make a query. First, add this as an argument to your form class::
-
- // src/Form/TaskType.php
- use Doctrine\ORM\EntityManagerInterface;
- // ...
-
- class TaskType extends AbstractType
- {
- private $entityManager;
-
- public function __construct(EntityManagerInterface $entityManager)
- {
- $this->entityManager = $entityManager;
- }
-
- // ...
- }
-
-If you're using :ref:`autowire ` and
-:ref:`autoconfigure `, then you don't need to do *anything*
-else: Symfony will automatically know how to pass the correct ``EntityManager`` object
-to your ``__construct()`` method.
-
-If you are **not using autowire and autoconfigure**, register your form as a service
-manually and tag it with ``form.type``:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # config/services.yaml
- services:
- App\Form\TaskType:
- arguments: ['@doctrine.orm.entity_manager']
- tags: [form.type]
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // config/services.php
- use App\Form\TaskType;
- use Symfony\Component\DependencyInjection\Reference;
-
- $container->register(TaskType::class)
- ->addArgument(new Reference('doctrine.orm.entity_manager'))
- ->addTag('form.type')
- ;
-
-That's it! Your controller - where you create the form - doesn't need to change
-at all: Symfony is smart enough to load the ``TaskType`` from the container.
-
-Read :ref:`form-field-service` for more information.
+The content of this article is no longer relevant because in current Symfony
+versions, form classes are services by default and you can inject services in
+them using the :doc:`service autowiring ` feature.
+
+Read the article about :doc:`creating custom form types `
+to see an example of how to inject the database service into a form type. In the
+same article you can also read about
+:ref:`configuration options for form types `, which is
+another way of passing services to forms.
diff --git a/form/use_empty_data.rst b/form/use_empty_data.rst
index 410f652acb4..c186396f8e4 100644
--- a/form/use_empty_data.rst
+++ b/form/use_empty_data.rst
@@ -75,7 +75,11 @@ The point is, you can set ``empty_data`` to the exact "new" object that you want
.. tip::
In order to pass arguments to the ``BlogType`` constructor, you'll need to
- :doc:`register it as a service and tag with form.type `.
+ :ref:`register the form as a service `
+ and :doc:`tag it ` with ``form.type``.
+ If you're using the
+ :ref:`default services.yaml configuration `,
+ this is already done for you.
.. _forms-empty-data-closure: