Skip to content

Commit

Permalink
Merge branch '2.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
weaverryan committed Mar 10, 2014
2 parents 0a65b6f + 0bc09d7 commit 8eaf232
Show file tree
Hide file tree
Showing 32 changed files with 629 additions and 81 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
13 changes: 10 additions & 3 deletions book/http_cache.rst
Original file line number Diff line number Diff line change
Expand Up @@ -572,9 +572,16 @@ To see a simple implementation, generate the ETag as the md5 of the content::
}

The :method:`Symfony\\Component\\HttpFoundation\\Response::isNotModified`
method compares the ``ETag`` sent with the ``Request`` with the one set
on the ``Response``. If the two match, the method automatically sets the
``Response`` status code to 304.
method compares the ``If-None-Match`` sent with the ``Request`` with the
``ETag`` header set on the ``Response``. If the two match, the method
automatically sets the ``Response`` status code to 304.

.. note::

The ``If-None-Match`` request header equals the ``ETag`` header of the
last response sent to the client for the particular resource. This is
how the client and server communicate with each other and decide whether
or not the resource has been updated since it was cached.

This algorithm is simple enough and very generic, but you need to create the
whole ``Response`` before being able to compute the ETag, which is sub-optimal.
Expand Down
9 changes: 6 additions & 3 deletions book/internals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,16 @@ Use the :method:`Symfony\\Component\\HttpKernel\\Profiler\\Profiler::find`
method to access tokens based on some criteria::

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

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

// get the latest 10 tokens for local requests
$tokens = $container->get('profiler')->find('127.0.0.1', '', 10);
$tokens = $container->get('profiler')->find('127.0.0.1', '', 10, '', '');

// get the latest 10 tokens for requests that happened between 2 and 4 days ago
$tokens = $container->get('profiler')->find('', '', 10, '4 days ago', '2 days ago');

If you want to manipulate profiling data on a different machine than the one
where the information were generated, use the
Expand Down
5 changes: 0 additions & 5 deletions book/routing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -912,11 +912,6 @@ that are special: each adds a unique piece of functionality inside your applicat

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

.. tip::

If you use the ``_locale`` parameter in a route, that value will also
be stored on the session so that subsequent requests keep this same locale.

.. index::
single: Routing; Controllers
single: Controller; String naming format
Expand Down
6 changes: 6 additions & 0 deletions book/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,12 @@ into your Symfony2 application::
The ``request()`` method takes the HTTP method and a URL as arguments and
returns a ``Crawler`` instance.

.. tip::

Hardcoding the request URLs is a best practice for functional tests. If the
test generates URLs using the Symfony router, it won't detect any change
made to the application URLs which may impact the end users.

Use the Crawler to find DOM elements in the Response. These elements can then
be used to click on links and submit forms::

Expand Down
2 changes: 1 addition & 1 deletion book/translation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ The translator service is accessible in PHP templates through the
<?php echo $view['translator']->trans('Symfony2 is great') ?>

<?php echo $view['translator']->transChoice(
'{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
'{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
10,
array('%count%' => 10)
) ?>
Expand Down
33 changes: 22 additions & 11 deletions book/validation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -807,11 +807,13 @@ user registers and when a user updates their contact information later:
}
}
With this configuration, there are two validation groups:
With this configuration, there are three validation groups:

* ``User`` - contains the constraints that belong to no other group,
and is considered the ``Default`` group. (This group is useful for
:ref:`book-validation-group-sequence`);
* ``Default`` - contains the constraints in the current class and all
referenced classes that belong to no other group;

* ``User`` - equivalent to all constraints of the ``User`` object in the
``Default`` group;

* ``registration`` - contains the constraints on the ``email`` and ``password``
fields only.
Expand All @@ -837,13 +839,8 @@ Group Sequence
--------------

In some cases, you want to validate your groups by steps. To do this, you can
use the ``GroupSequence`` feature. In the case, an object defines a group sequence,
and then the groups in the group sequence are validated in order.

.. tip::

Group sequences cannot contain the group ``Default``, as this would create
a loop. Instead, use the group ``{ClassName}`` (e.g. ``User``).
use the ``GroupSequence`` feature. In this case, an object defines a group sequence
, which determines the order groups should be validated.

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

.. caution::

As you have already seen in the previous section, the ``Default`` group
and the group containing the class name (e.g. ``User``) were identical.
However, when using Group Sequences, they are no longer identical. The
``Default`` group will now reference the group sequence, instead of all
constraints that do not belong to any group.

This means that you have to use the ``{ClassName}`` (e.g. ``User``) group
when specifing a group sequence. When using ``Default``, you get an
infinite recursion (as the ``Default`` groups references the group
sequence, which will contain the ``Default`` group which references the
same group sequence, ...).

Group Sequence Providers
~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ documentation.
.. tip::

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

January, 2014
-------------
Expand Down
1 change: 1 addition & 0 deletions components/form/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
:maxdepth: 2

introduction
type_guesser
191 changes: 191 additions & 0 deletions components/form/type_guesser.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
.. index::
single: Forms; Custom Type Guesser

Creating a Custom Type Guesser
==============================

The Form component can guess the type and some options of a form field by
using type guessers. The component already includes a type guesser using the
assertions of the Validation component, but you can also add your own custom
type guessers.

.. sidebar:: Form Type Guessers in the Bridges

Symfony also provides some form type guessers in the bridges:

* :class:`Symfony\\Bridge\\Propel1\\Form\\PropelTypeGuesser` provided by
the Propel1 bridge;
* :class:`Symfony\\Bridge\\Doctrine\\Form\\DoctrineOrmTypeGuesser`
provided by the Doctrine bridge.

Create a PHPDoc Type Guesser
----------------------------

In this section, you are going to build a guesser that reads information about
fields from the PHPDoc of the properties. At first, you need to create a class
which implements :class:`Symfony\\Component\\Form\\FormTypeGuesserInterface`.
This interface requires 4 methods:

* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessType` -
tries to guess the type of a field;
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessRequired` -
tries to guess the value of the :ref:`required <reference-form-option-required>`
option;
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessMaxLength` -
tries to guess the value of the :ref:`max_length <reference-form-option-max_length>`
option;
* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessPattern` -
tries to guess the value of the :ref:`pattern <reference-form-option-pattern>`
option.

Start by creating the class and these methods. Next, you'll learn how to fill each on.

.. code-block:: php
namespace Acme\Form;
use Symfony\Component\Form\FormTypeGuesserInterface;
class PhpdocTypeGuesser implements FormTypeGuesserInterface
{
public function guessType($class, $property)
{
}
public function guessRequired($class, $property)
{
}
public function guessMaxLength($class, $property)
{
}
public function guessPattern($class, $property)
{
}
}
Guessing the Type
~~~~~~~~~~~~~~~~~

When guessing a type, the method returns either an instance of
:class:`Symfony\\Component\\Form\\Guess\\TypeGuess` or nothing, to determine
that the type guesser cannot guess the type.

The ``TypeGuess`` constructor requires 3 options:

* The type name (one of the :doc:`form types </reference/forms/types`);
* Additional options (for instance, when the type is ``entity``, you also
want to set the ``class`` option). If no types are guessed, this should be
set to an empty array;
* The confidence that the guessed type is correct. This can be one of the
constants of the :class:`Symfony\\Component\\Form\\Guess\Guess` class:
``LOW_CONFIDENCE``, ``MEDIUM_CONFIDENCE``, ``HIGH_CONFIDENCE``,
``VERY_HIGH_CONFIDENCE``. After all type guessers have been executed, the
type with the highest confidence is used.

With this knowledge, you can easily implement the ``guessType`` method of the
``PHPDocTypeGuesser``::

namespace Acme\Form;

use Symfony\Component\Form\Guess\Guess;
use Symfony\Component\Form\Guess\TypeGuess;

class PhpdocTypeGuesser implements FormTypeGuesserInterface
{
public function guessType($class, $property)
{
$annotations = $this->readPhpDocAnnotations($class, $property);

if (!isset($annotations['var'])) {
return; // guess nothing if the @var annotation is not available
}

// otherwise, base the type on the @var annotation
switch ($annotations['var']) {
case 'string':
// there is a high confidence that the type is a string when
// @var string is used
return new TypeGuess('text', array(), Guess::HIGH_CONFIDENCE);

case 'int':
case 'integer':
// integers can also be the id of an entity or a checkbox (0 or 1)
return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE);

case 'float':
case 'double':
case 'real':
return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE);

case 'boolean':
case 'bool':
return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE);

default:
// there is a very low confidence that this one is correct
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
}
}

protected function readPhpDocAnnotations($class, $property)
{
$reflectionProperty = new \ReflectionProperty($class, $property);
$phpdoc = $reflectionProperty->getDocComment();

// parse the $phpdoc into an array like:
// array('type' => 'string', 'since' => '1.0')
$phpdocTags = ...;

return $phpdocTags;
}
}

This type guesser can now guess the field type for a property if it has
PHPdoc!

Guessing Field Options
~~~~~~~~~~~~~~~~~~~~~~

The other 3 methods (``guessMaxLength``, ``guessRequired`` and
``guessPattern``) return a :class:`Symfony\\Component\\Form\\Guess\\ValueGuess`
instance with the value of the option. This constructor has 2 arguments:

* The value of the option;
* The confidence that the guessed value is correct (using the constants of the
``Guess`` class).

``null`` is guessed when you believe the value of the option should not be
set.

.. caution::

You should be very careful using the ``guessPattern`` method. When the
type is a float, you cannot use it to determine a min or max value of the
float (e.g. you want a float to be greater than ``5``, ``4.512313`` is not valid
but ``length(4.512314) > length(5)`` is, so the pattern will succeed). In
this case, the value should be set to ``null`` with a ``MEDIUM_CONFIDENCE``.

Registering a Type Guesser
--------------------------

The last thing you need to do is registering your custom type guesser by using
:method:`Symfony\\Component\\Form\\FormFactoryBuilder::addTypeGuesser` or
:method:`Symfony\\Component\\Form\\FormFactoryBuilder::addTypeGuessers`::

use Symfony\Component\Form\Forms;
use Acme\Form\PHPDocTypeGuesser;

$formFactory = Forms::createFormFactoryBuilder()
// ...
->addTypeGuesser(new PHPDocTypeGuesser())
->getFormFactory();

// ...

.. note::

When you use the Symfony framework, you need to register your type guesser
and tag it with ``form.type_guesser``. For more information see
:ref:`the tag reference <reference-dic-type_guesser>`.
12 changes: 10 additions & 2 deletions components/http_foundation/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,22 @@ argument::

.. _component-foundation-attributes:

Finally, you can also store additional data in the request,
thanks to the public ``attributes`` property, which is also an instance of
Thanks to the public ``attributes`` property, you can store additional data
in the request, which is also an instance of
:class:`Symfony\\Component\\HttpFoundation\\ParameterBag`. This is mostly used
to attach information that belongs to the Request and that needs to be
accessed from many different points in your application. For information
on how this is used in the Symfony2 framework, see
:ref:`the Symfony2 book <book-fundamentals-attributes>`.

Finally, the raw data sent with the request body can be accessed using
:method:`Symfony\\Component\\HttpFoundation\\Request::getContent()`::

$content = $request->getContent();

For instance, this may be useful to process a JSON string sent to the
application by a remote service using the HTTP POST method.

Identifying a Request
~~~~~~~~~~~~~~~~~~~~~

Expand Down
1 change: 1 addition & 0 deletions components/map.rst.inc
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
* :doc:`/components/form/index`

* :doc:`/components/form/introduction`
* :doc:`/components/form/type_guesser`

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

Expand Down
Loading

0 comments on commit 8eaf232

Please sign in to comment.