Skip to content

Commit

Permalink
feature #5909 More 2.8 form updates (weaverryan)
Browse files Browse the repository at this point in the history
This PR was merged into the 2.8 branch.

Discussion
----------

More 2.8 form updates

| Q             | A
|------------- | ---
| Doc fix?      | yes
| New docs?     | no
| Applies to    | 2.8+
| Fixed tickets | #5588

This is addition to what was done in #5834. These changes are HUGE, so if anyone can review this for copy-paste changes or other mistakes, that would be awesome. Also, if you can think of any other things to search for the the code base and are able to find MORE instances, let me know.

Commits
-------

d131449 several other tweaks
feb68dd Completely updating the form type reference section for the text -> TextType changes
832a12a fixing build error
3117df5 Another huge batch of changes for the form type changes in 2.8
  • Loading branch information
weaverryan committed Nov 30, 2015
2 parents b094312 + d131449 commit 3a59f72
Show file tree
Hide file tree
Showing 57 changed files with 554 additions and 515 deletions.
21 changes: 2 additions & 19 deletions best_practices/forms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ some developers configure form buttons in the controller::

use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use AppBundle\Entity\Post;
use AppBundle\Form\PostType;

Expand All @@ -121,7 +122,7 @@ some developers configure form buttons in the controller::
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
$form->add('submit', 'submit', array(
$form->add('submit', SubmitType::class, array(
'label' => 'Create',
'attr' => array('class' => 'btn btn-default pull-right')
));
Expand Down Expand Up @@ -205,21 +206,3 @@ Second, we recommend using ``$form->isSubmitted()`` in the ``if`` statement
for clarity. This isn't technically needed, since ``isValid()`` first calls
``isSubmitted()``. But without this, the flow doesn't read well as it *looks*
like the form is *always* processed (even on the GET request).

Custom Form Field Types
-----------------------

.. best-practice::

Add the ``app_`` prefix to your custom form field types to avoid collisions.

Custom form field types inherit from the ``AbstractType`` class, which defines the
``getName()`` method to configure the name of that form type. These names must
be unique in the application.

If a custom form type uses the same name as any of the Symfony's built-in form
types, it will override it. The same happens when the custom form type matches
any of the types defined by the third-party bundles installed in your application.

Add the ``app_`` prefix to your custom form field types to avoid name collisions
that can lead to hard to debug errors.
12 changes: 8 additions & 4 deletions book/forms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,10 @@ which HTML form tag(s) is rendered for that field.

.. versionadded:: 2.8
To denote the form type, you have to use the fully qualified class name - like
TextType::class in PHP 5.5+ or ``Symfony\Component\Form\Extension\Core\Type\TextType``.
``TextType::class`` in PHP 5.5+ or ``Symfony\Component\Form\Extension\Core\Type\TextType``.
Before Symfony 2.8, you could use an alias for each type like ``text`` or
``date``. For more details, see the `2.8 UPGRADE Log`_.
``date``. The old alias syntax will still work until Symfony 3.0. For more details,
see the `2.8 UPGRADE Log`_.

Finally, you added a submit button with a custom label for submitting the form to
the server.
Expand Down Expand Up @@ -1005,7 +1006,7 @@ ways. If you build your form in the controller, you can use ``setAction()`` and
->setAction($this->generateUrl('target_route'))
->setMethod('GET')
->add('task', TextType::class)
->add('dueDate', DateType::clas)
->add('dueDate', DateType::class)
->add('save', SubmitType::class)
->getForm();

Expand All @@ -1018,7 +1019,10 @@ In :ref:`book-form-creating-form-classes` you will learn how to move the
form building code into separate classes. When using an external form class
in the controller, you can pass the action and method as form options::

$form = $this->createForm(new TaskType(), $task, array(
use AppBundle\Form\Type\TaskType;
// ...

$form = $this->createForm(TaskType::class, $task, array(
'action' => $this->generateUrl('target_route'),
'method' => 'GET',
));
Expand Down
2 changes: 1 addition & 1 deletion book/validation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ workflow looks like the following from inside a controller::
public function updateAction(Request $request)
{
$author = new Author();
$form = $this->createForm(new AuthorType(), $author);
$form = $this->createForm(AuthorType::class, $author);

$form->handleRequest($request);

Expand Down
15 changes: 7 additions & 8 deletions cookbook/bundles/override.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,16 @@ associations. Learn more about this feature and its limitations in
Forms
-----

In order to override a form type, it has to be registered as a service (meaning
it is tagged as ``form.type``). You can then override it as you would override any
service as explained in `Services & Configuration`_. This, of course, will only
work if the type is referred to by its alias rather than being instantiated,
e.g.::
As of Symfony 2.8, form types are referred to by their fully-qualified class name::

$builder->add('name', 'custom_type');
$builder->add('name', CustomType::class);

rather than::
This means that you cannot override this by creating a sub-class of ``CustomType``
and registering it as a service, and tagging it with ``form.type`` (you *could*
do this in earlier version).

$builder->add('name', new CustomType());
Instead, you should use a "form type extension" to modify the existing form type.
For more information, see :doc:`/cookbook/form/create_form_type_extension`.

.. _override-validation:

Expand Down
5 changes: 3 additions & 2 deletions cookbook/controller/upload_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,15 @@ Then, add a new ``brochure`` field to the form that manages the ``Product`` enti
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;

class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// ...
->add('brochure', 'file', array('label' => 'Brochure (PDF file)'))
->add('brochure', FileType::class, array('label' => 'Brochure (PDF file)'))
// ...
;
}
Expand Down Expand Up @@ -116,7 +117,7 @@ Finally, you need to update the code of the controller that handles the form::
public function newAction(Request $request)
{
$product = new Product();
$form = $this->createForm(new ProductType(), $product);
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);

if ($form->isValid()) {
Expand Down
69 changes: 34 additions & 35 deletions cookbook/form/create_custom_field_type.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ for form fields, which is ``<BundleName>\Form\Type``. Make sure the field extend

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

class GenderType extends AbstractType
{
Expand All @@ -40,12 +41,7 @@ for form fields, which is ``<BundleName>\Form\Type``. Make sure the field extend

public function getParent()
{
return 'choice';
}

public function getName()
{
return 'gender';
return ChoiceType::class;
}
}

Expand All @@ -54,8 +50,12 @@ for form fields, which is ``<BundleName>\Form\Type``. Make sure the field extend
The location of this file is not important - the ``Form\Type`` directory
is just a convention.

.. versionadded:: 2.8
In 2.8, the ``getName()`` method was removed. Now, fields are always referred
to by their fully-qualified class name.

Here, the return value of the ``getParent`` function indicates that you're
extending the ``choice`` field type. This means that, by default, you inherit
extending the ``ChoiceType`` field. This means that, by default, you inherit
all of the logic and rendering of that field type. To see some of the logic,
check out the `ChoiceType`_ class. There are three methods that are particularly
important:
Expand Down Expand Up @@ -89,24 +89,26 @@ important:
Also, if you need to modify the "view" of any of your child types from
your parent type, use the ``finishView()`` method.

The ``getName()`` method returns an identifier which should be unique in
your application. This is used in various places, such as when customizing
how your form type will be rendered.

The goal of this field was to extend the choice type to enable selection of
a gender. This is achieved by fixing the ``choices`` to a list of possible
genders.

Creating a Template for the Field
---------------------------------

Each field type is rendered by a template fragment, which is determined in
part by the value of your ``getName()`` method. For more information, see
Each field type is rendered by a template fragment, which is determined in part by
the class name of your type. For more information, see
:ref:`cookbook-form-customization-form-themes`.

In this case, since the parent field is ``choice``, you don't *need* to do
any work as the custom field type will automatically be rendered like a ``choice``
type. But for the sake of this example, suppose that when your field is "expanded"
.. note::

The first part of the prefix (e.g. ``gender``) comes from the class name
(``GenderType`` -> ``gender``). This can be controlled by overriding ``getBlockPrefix()``
in ``GenderType``.

In this case, since the parent field is ``ChoiceType``, you don't *need* to do
any work as the custom field type will automatically be rendered like a ``ChoiceType``.
But for the sake of this example, suppose that when your field is "expanded"
(i.e. radio buttons or checkboxes, instead of a select field), you want to
always render it in a ``ul`` element. In your form theme template (see above
link for details), create a ``gender_widget`` block to handle this:
Expand Down Expand Up @@ -154,7 +156,7 @@ link for details), create a ``gender_widget`` block to handle this:
.. note::

Make sure the correct widget prefix is used. In this example the name should
be ``gender_widget``, according to the value returned by ``getName``.
be ``gender_widget`` (see :ref:`cookbook-form-customization-form-themes`).
Further, the main config file should point to the custom form template
so that it's used when rendering all forms.

Expand Down Expand Up @@ -241,18 +243,19 @@ new instance of the type in one of your forms::

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use AppBundle\Form\Type\GenderType;

class AuthorType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('gender_code', new GenderType(), array(
$builder->add('gender_code', GenderType::class, array(
'placeholder' => 'Choose a gender',
));
}
}

But this only works because the ``GenderType()`` is very simple. What if
But this only works because the ``GenderType`` is very simple. What if
the gender codes were stored in configuration or in a database? The next
section explains how more complex field types solve this problem.

Expand Down Expand Up @@ -311,14 +314,14 @@ the ``genders`` parameter value as the first argument to its to-be-created
arguments:
- "%genders%"
tags:
- { name: form.type, alias: gender }
- { name: form.type }
.. code-block:: xml
<!-- src/AppBundle/Resources/config/services.xml -->
<service id="app.form.type.gender" class="AppBundle\Form\Type\GenderType">
<argument>%genders%</argument>
<tag name="form.type" alias="gender" />
<tag name="form.type" />
</service>
.. code-block:: php
Expand All @@ -331,20 +334,16 @@ the ``genders`` parameter value as the first argument to its to-be-created
'AppBundle\Form\Type\GenderType',
array('%genders%')
))
->addTag('form.type', array(
'alias' => 'gender',
))
->addTag('form.type')
;
.. tip::

Make sure the services file is being imported. See :ref:`service-container-imports-directive`
for details.

Be sure that the ``alias`` attribute of the tag corresponds with the value
returned by the ``getName`` method defined earlier. You'll see the importance
of this in a moment when you use the custom field type. But first, add a ``__construct``
method to ``GenderType``, which receives the gender configuration::
First, add a ``__construct`` method to ``GenderType``, which receives the gender
configuration::

// src/AppBundle/Form/Type/GenderType.php
namespace AppBundle\Form\Type;
Expand Down Expand Up @@ -374,28 +373,28 @@ method to ``GenderType``, which receives the gender configuration::
}

Great! The ``GenderType`` is now fueled by the configuration parameters and
registered as a service. Additionally, because you used the ``form.type`` alias in its
configuration, using the field is now much easier::
registered as a service. Because you used the ``form.type`` alias in its configuration,
your service will be used instead of creating a *new* ``GenderType``. In other words,
your controller *does not need to change*, it still looks like this::

// src/AppBundle/Form/Type/AuthorType.php
namespace AppBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

// ...
use AppBundle\Form\Type\GenderType;

class AuthorType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('gender_code', 'gender', array(
$builder->add('gender_code', GenderType::class, array(
'placeholder' => 'Choose a gender',
));
}
}

Notice that instead of instantiating a new instance, you can just refer to
it by the alias used in your service configuration, ``gender``. Have fun!
Have fun!

.. _`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
Loading

0 comments on commit 3a59f72

Please sign in to comment.