Skip to content

Commit 2657ee7

Browse files
committed
feature #3597 Document how to create a custom type guesser (WouterJ)
This PR was merged into the 2.3 branch. Discussion ---------- Document how to create a custom type guesser | Q | A | --- | --- | Doc fix? | no | New docs? | yes | Applies to | all | Fixed tickets | #481 Commits ------- 0d37a3b Applied comments 3b3cd6f Added references f36fdb7 Documented FormTypeGuesserInterface
2 parents 5ad1599 + 0d37a3b commit 2657ee7

File tree

5 files changed

+206
-6
lines changed

5 files changed

+206
-6
lines changed

components/form/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
:maxdepth: 2
66

77
introduction
8+
type_guesser

components/form/type_guesser.rst

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
.. index::
2+
single: Forms; Custom Type Guesser
3+
4+
Creating a Custom Type Guesser
5+
==============================
6+
7+
The Form component can guess the type and some options of a form field by
8+
using type guessers. The component already includes a type guesser using the
9+
assertions of the Validation component, but you can also add your own custom
10+
type guessers.
11+
12+
.. sidebar:: Form Type Guessers in the Bridges
13+
14+
Symfony also provides some form type guessers in the bridges:
15+
16+
* :class:`Symfony\\Bridge\\Propel1\\Form\\PropelTypeGuesser` provided by
17+
the Propel1 bridge;
18+
* :class:`Symfony\\Bridge\\Doctrine\\Form\\DoctrineOrmTypeGuesser`
19+
provided by the Doctrine bridge.
20+
21+
Create a PHPDoc Type Guesser
22+
----------------------------
23+
24+
In this section, you are going to build a guesser that reads information about
25+
fields from the PHPDoc of the properties. At first, you need to create a class
26+
which implements :class:`Symfony\\Component\\Form\\FormTypeGuesserInterface`.
27+
This interface requires 4 methods:
28+
29+
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessType` -
30+
tries to guess the type of a field;
31+
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessRequired` -
32+
tries to guess the value of the :ref:`required <reference-form-option-required>`
33+
option;
34+
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessMaxLength` -
35+
tries to guess the value of the :ref:`max_length <reference-form-option-max_length>`
36+
option;
37+
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessPattern` -
38+
tries to guess the value of the :ref:`pattern <reference-form-option-pattern>`
39+
option.
40+
41+
Start by creating the class and these methods. Next, you'll learn how to fill each on.
42+
43+
.. code-block:: php
44+
45+
namespace Acme\Form;
46+
47+
use Symfony\Component\Form\FormTypeGuesserInterface;
48+
49+
class PhpdocTypeGuesser implements FormTypeGuesserInterface
50+
{
51+
public function guessType($class, $property)
52+
{
53+
}
54+
55+
public function guessRequired($class, $property)
56+
{
57+
}
58+
59+
public function guessMaxLength($class, $property)
60+
{
61+
}
62+
63+
public function guessPattern($class, $property)
64+
{
65+
}
66+
}
67+
68+
Guessing the Type
69+
~~~~~~~~~~~~~~~~~
70+
71+
When guessing a type, the method returns either an instance of
72+
:class:`Symfony\\Component\\Form\\Guess\\TypeGuess` or nothing, to determine
73+
that the type guesser cannot guess the type.
74+
75+
The ``TypeGuess`` constructor requires 3 options:
76+
77+
* The type name (one of the :doc:`form types </reference/forms/types`);
78+
* Additional options (for instance, when the type is ``entity``, you also
79+
want to set the ``class`` option). If no types are guessed, this should be
80+
set to an empty array;
81+
* The confidence that the guessed type is correct. This can be one of the
82+
constants of the :class:`Symfony\\Component\\Form\\Guess\Guess` class:
83+
``LOW_CONFIDENCE``, ``MEDIUM_CONFIDENCE``, ``HIGH_CONFIDENCE``,
84+
``VERY_HIGH_CONFIDENCE``. After all type guessers have been executed, the
85+
type with the highest confidence is used.
86+
87+
With this knowledge, you can easily implement the ``guessType`` method of the
88+
``PHPDocTypeGuesser``::
89+
90+
namespace Acme\Form;
91+
92+
use Symfony\Component\Form\Guess\Guess;
93+
use Symfony\Component\Form\Guess\TypeGuess;
94+
95+
class PhpdocTypeGuesser implements FormTypeGuesserInterface
96+
{
97+
public function guessType($class, $property)
98+
{
99+
$annotations = $this->readPhpDocAnnotations($class, $property);
100+
101+
if (!isset($annotations['var'])) {
102+
return; // guess nothing if the @var annotation is not available
103+
}
104+
105+
// otherwise, base the type on the @var annotation
106+
switch ($annotations['var']) {
107+
case 'string':
108+
// there is a high confidence that the type is a string when
109+
// @var string is used
110+
return new TypeGuess('text', array(), Guess::HIGH_CONFIDENCE);
111+
112+
case 'int':
113+
case 'integer':
114+
// integers can also be the id of an entity or a checkbox (0 or 1)
115+
return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE);
116+
117+
case 'float':
118+
case 'double':
119+
case 'real':
120+
return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE);
121+
122+
case 'boolean':
123+
case 'bool':
124+
return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE);
125+
126+
default:
127+
// there is a very low confidence that this one is correct
128+
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
129+
}
130+
}
131+
132+
protected function readPhpDocAnnotations($class, $property)
133+
{
134+
$reflectionProperty = new \ReflectionProperty($class, $property);
135+
$phpdoc = $reflectionProperty->getDocComment();
136+
137+
// parse the $phpdoc into an array like:
138+
// array('type' => 'string', 'since' => '1.0')
139+
$phpdocTags = ...;
140+
141+
return $phpdocTags;
142+
}
143+
}
144+
145+
This type guesser can now guess the field type for a property if it has
146+
PHPdoc!
147+
148+
Guessing Field Options
149+
~~~~~~~~~~~~~~~~~~~~~~
150+
151+
The other 3 methods (``guessMaxLength``, ``guessRequired`` and
152+
``guessPattern``) return a :class:`Symfony\\Component\\Form\\Guess\\ValueGuess`
153+
instance with the value of the option. This constructor has 2 arguments:
154+
155+
* The value of the option;
156+
* The confidence that the guessed value is correct (using the constants of the
157+
``Guess`` class).
158+
159+
``null`` is guessed when you believe the value of the option should not be
160+
set.
161+
162+
.. caution::
163+
164+
You should be very careful using the ``guessPattern`` method. When the
165+
type is a float, you cannot use it to determine a min or max value of the
166+
float (e.g. you want a float to be greater than ``5``, ``4.512313`` is not valid
167+
but ``length(4.512314) > length(5)`` is, so the pattern will succeed). In
168+
this case, the value should be set to ``null`` with a ``MEDIUM_CONFIDENCE``.
169+
170+
Registering a Type Guesser
171+
--------------------------
172+
173+
The last thing you need to do is registering your custom type guesser by using
174+
:method:`Symfony\\Component\\Form\\FormFactoryBuilder::addTypeGuesser` or
175+
:method:`Symfony\\Component\\Form\\FormFactoryBuilder::addTypeGuessers`::
176+
177+
use Symfony\Component\Form\Forms;
178+
use Acme\Form\PHPDocTypeGuesser;
179+
180+
$formFactory = Forms::createFormFactoryBuilder()
181+
// ...
182+
->addTypeGuesser(new PHPDocTypeGuesser())
183+
->getFormFactory();
184+
185+
// ...
186+
187+
.. note::
188+
189+
When you use the Symfony framework, you need to register your type guesser
190+
and tag it with ``form.type_guesser``. For more information see
191+
:ref:`the tag reference <reference-dic-type_guesser>`.

components/map.rst.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
* :doc:`/components/form/index`
6969

7070
* :doc:`/components/form/introduction`
71+
* :doc:`/components/form/type_guesser`
7172

7273
* :doc:`/components/http_foundation/index`
7374

reference/dic_tags.rst

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -332,21 +332,22 @@ The ``alias`` key of the tag is the type of field that this extension should
332332
be applied to. For example, to apply the extension to any form/field, use the
333333
"form" value.
334334
335+
.. _reference-dic-type_guesser:
336+
335337
form.type_guesser
336338
-----------------
337339
338340
**Purpose**: Add your own logic for "form type guessing"
339341
340342
This tag allows you to add your own logic to the :ref:`Form Guessing <book-forms-field-guessing>`
341343
process. By default, form guessing is done by "guessers" based on the validation
342-
metadata and Doctrine metadata (if you're using Doctrine).
344+
metadata and Doctrine metadata (if you're using Doctrine) or Propel metadata
345+
(if you're using Propel).
343346
344-
To add your own form type guesser, create a class that implements the
345-
:class:`Symfony\\Component\\Form\\FormTypeGuesserInterface` interface. Next,
346-
tag its service definition with ``form.type_guesser`` (it has no options).
347+
.. seelalso::
347348
348-
To see an example of how this class might look, see the ``ValidatorTypeGuesser``
349-
class in the Form component.
349+
For information on how to create your own type guesser, see
350+
:doc:`/components/form/type_guesser`.
350351
351352
kernel.cache_clearer
352353
--------------------

reference/forms/types/form.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ on all fields.
1717

1818
.. include:: /reference/forms/types/options/empty_data.rst.inc
1919

20+
.. _reference-form-option-required:
21+
2022
.. include:: /reference/forms/types/options/required.rst.inc
2123

2224
.. include:: /reference/forms/types/options/label.rst.inc
@@ -43,6 +45,8 @@ on all fields.
4345

4446
.. include:: /reference/forms/types/options/block_name.rst.inc
4547

48+
.. _reference-form-option-max_length:
49+
4650
.. include:: /reference/forms/types/options/max_length.rst.inc
4751

4852
.. include:: /reference/forms/types/options/by_reference.rst.inc
@@ -61,6 +65,8 @@ on all fields.
6165

6266
.. include:: /reference/forms/types/options/post_max_size_message.rst.inc
6367

68+
.. _reference-form-option-pattern:
69+
6470
.. include:: /reference/forms/types/options/pattern.rst.inc
6571

6672
.. include:: /reference/forms/types/options/action.rst.inc

0 commit comments

Comments
 (0)