Skip to content

Commit 8eaf232

Browse files
committed
Merge branch '2.4'
2 parents 0a65b6f + 0bc09d7 commit 8eaf232

32 files changed

+629
-81
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store

book/http_cache.rst

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -572,9 +572,16 @@ To see a simple implementation, generate the ETag as the md5 of the content::
572572
}
573573

574574
The :method:`Symfony\\Component\\HttpFoundation\\Response::isNotModified`
575-
method compares the ``ETag`` sent with the ``Request`` with the one set
576-
on the ``Response``. If the two match, the method automatically sets the
577-
``Response`` status code to 304.
575+
method compares the ``If-None-Match`` sent with the ``Request`` with the
576+
``ETag`` header set on the ``Response``. If the two match, the method
577+
automatically sets the ``Response`` status code to 304.
578+
579+
.. note::
580+
581+
The ``If-None-Match`` request header equals the ``ETag`` header of the
582+
last response sent to the client for the particular resource. This is
583+
how the client and server communicate with each other and decide whether
584+
or not the resource has been updated since it was cached.
578585

579586
This algorithm is simple enough and very generic, but you need to create the
580587
whole ``Response`` before being able to compute the ETag, which is sub-optimal.

book/internals.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,13 +521,16 @@ Use the :method:`Symfony\\Component\\HttpKernel\\Profiler\\Profiler::find`
521521
method to access tokens based on some criteria::
522522

523523
// get the latest 10 tokens
524-
$tokens = $container->get('profiler')->find('', '', 10);
524+
$tokens = $container->get('profiler')->find('', '', 10, '', '');
525525

526526
// get the latest 10 tokens for all URL containing /admin/
527-
$tokens = $container->get('profiler')->find('', '/admin/', 10);
527+
$tokens = $container->get('profiler')->find('', '/admin/', 10, '', '');
528528

529529
// get the latest 10 tokens for local requests
530-
$tokens = $container->get('profiler')->find('127.0.0.1', '', 10);
530+
$tokens = $container->get('profiler')->find('127.0.0.1', '', 10, '', '');
531+
532+
// get the latest 10 tokens for requests that happened between 2 and 4 days ago
533+
$tokens = $container->get('profiler')->find('', '', 10, '4 days ago', '2 days ago');
531534

532535
If you want to manipulate profiling data on a different machine than the one
533536
where the information were generated, use the

book/routing.rst

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -912,11 +912,6 @@ that are special: each adds a unique piece of functionality inside your applicat
912912

913913
* ``_locale``: Used to set the locale on the request (:ref:`read more <book-translation-locale-url>`);
914914

915-
.. tip::
916-
917-
If you use the ``_locale`` parameter in a route, that value will also
918-
be stored on the session so that subsequent requests keep this same locale.
919-
920915
.. index::
921916
single: Routing; Controllers
922917
single: Controller; String naming format

book/testing.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,12 @@ into your Symfony2 application::
327327
The ``request()`` method takes the HTTP method and a URL as arguments and
328328
returns a ``Crawler`` instance.
329329

330+
.. tip::
331+
332+
Hardcoding the request URLs is a best practice for functional tests. If the
333+
test generates URLs using the Symfony router, it won't detect any change
334+
made to the application URLs which may impact the end users.
335+
330336
Use the Crawler to find DOM elements in the Response. These elements can then
331337
be used to click on links and submit forms::
332338

book/translation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ The translator service is accessible in PHP templates through the
318318
<?php echo $view['translator']->trans('Symfony2 is great') ?>
319319

320320
<?php echo $view['translator']->transChoice(
321-
'{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
321+
'{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
322322
10,
323323
array('%count%' => 10)
324324
) ?>

book/validation.rst

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -807,11 +807,13 @@ user registers and when a user updates their contact information later:
807807
}
808808
}
809809
810-
With this configuration, there are two validation groups:
810+
With this configuration, there are three validation groups:
811811

812-
* ``User`` - contains the constraints that belong to no other group,
813-
and is considered the ``Default`` group. (This group is useful for
814-
:ref:`book-validation-group-sequence`);
812+
* ``Default`` - contains the constraints in the current class and all
813+
referenced classes that belong to no other group;
814+
815+
* ``User`` - equivalent to all constraints of the ``User`` object in the
816+
``Default`` group;
815817

816818
* ``registration`` - contains the constraints on the ``email`` and ``password``
817819
fields only.
@@ -837,13 +839,8 @@ Group Sequence
837839
--------------
838840

839841
In some cases, you want to validate your groups by steps. To do this, you can
840-
use the ``GroupSequence`` feature. In the case, an object defines a group sequence,
841-
and then the groups in the group sequence are validated in order.
842-
843-
.. tip::
844-
845-
Group sequences cannot contain the group ``Default``, as this would create
846-
a loop. Instead, use the group ``{ClassName}`` (e.g. ``User``).
842+
use the ``GroupSequence`` feature. In this case, an object defines a group sequence
843+
, which determines the order groups should be validated.
847844

848845
For example, suppose you have a ``User`` class and want to validate that the
849846
username and the password are different only if all other validation passes
@@ -968,6 +965,20 @@ In this example, it will first validate all constraints in the group ``User``
968965
(which is the same as the ``Default`` group). Only if all constraints in
969966
that group are valid, the second group, ``Strict``, will be validated.
970967

968+
.. caution::
969+
970+
As you have already seen in the previous section, the ``Default`` group
971+
and the group containing the class name (e.g. ``User``) were identical.
972+
However, when using Group Sequences, they are no longer identical. The
973+
``Default`` group will now reference the group sequence, instead of all
974+
constraints that do not belong to any group.
975+
976+
This means that you have to use the ``{ClassName}`` (e.g. ``User``) group
977+
when specifing a group sequence. When using ``Default``, you get an
978+
infinite recursion (as the ``Default`` groups references the group
979+
sequence, which will contain the ``Default`` group which references the
980+
same group sequence, ...).
981+
971982
Group Sequence Providers
972983
~~~~~~~~~~~~~~~~~~~~~~~~
973984

changelog.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ documentation.
1111
.. tip::
1212

1313
Do you also want to participate in the Symfony Documentation? Take a look
14-
at the ":doc:`/contributing/documentation`" article.
14+
at the ":doc:`/contributing/documentation/overview`" article.
1515

1616
January, 2014
1717
-------------

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

0 commit comments

Comments
 (0)