From f36fdb79d617b905f1c2730acf7c229e107fae26 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 22 Feb 2014 11:31:55 +0100 Subject: [PATCH 1/3] Documented FormTypeGuesserInterface --- components/form/index.rst | 1 + components/form/type_guesser.rst | 146 +++++++++++++++++++++++++++++++ components/map.rst.inc | 1 + 3 files changed, 148 insertions(+) create mode 100644 components/form/type_guesser.rst diff --git a/components/form/index.rst b/components/form/index.rst index 8283298b120..5ea5add00f6 100644 --- a/components/form/index.rst +++ b/components/form/index.rst @@ -5,3 +5,4 @@ :maxdepth: 2 introduction + type_guesser diff --git a/components/form/type_guesser.rst b/components/form/type_guesser.rst new file mode 100644 index 00000000000..57e86d44faa --- /dev/null +++ b/components/form/type_guesser.rst @@ -0,0 +1,146 @@ +.. 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. These can be + used if you use that library. + + * :class:`Symfony\\Bridge\\Propel1\\Form\\PropelTypeGuesser` provided by + the Propel1 bridge; + * :class:`Symfony\\Bridge\\Doctrine\\Form\\DoctrineOrmTypeGuesser` + provided by the Doctrine bridge. + +A PHPDoc Type Guesser +--------------------- + +In this section, you are going to build a PHPDoc type guesser. At first, you +need to create a class which extends +: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 ``required`` option; +* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessMaxLength` - + tries to guess the value of the ``max_length`` option; +* :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessPattern` - + tries to guess the value of the ``pattern`` option. + +The most basic class looks like:: + + 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 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); + } + } + +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 with 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 success). In + this case, the value should be set to ``null`` with a ``MEDIUM_CONFIDENCE``. diff --git a/components/map.rst.inc b/components/map.rst.inc index 867f04568ca..fbc1872afb8 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -68,6 +68,7 @@ * :doc:`/components/form/index` * :doc:`/components/form/introduction` + * :doc:`/components/form/type_guesser` * :doc:`/components/http_foundation/index` From 3b3cd6fee7a387f3e4389053fa2c280d8f6f7aa4 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 22 Feb 2014 11:39:03 +0100 Subject: [PATCH 2/3] Added references --- components/form/type_guesser.rst | 23 +++++++++++++++++++++++ reference/dic_tags.rst | 13 +++++++------ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/components/form/type_guesser.rst b/components/form/type_guesser.rst index 57e86d44faa..5cdacf77f4a 100644 --- a/components/form/type_guesser.rst +++ b/components/form/type_guesser.rst @@ -144,3 +144,26 @@ set. 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 success). 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 full stack framework, you need to register your type + guesser and tag it with ``form.type_guesser``. For more information see + :ref:`the tag reference `. diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 94fc8bac3f2..cbb8ca4c6a9 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -332,6 +332,8 @@ The ``alias`` key of the tag is the type of field that this extension should be applied to. For example, to apply the extension to any form/field, use the "form" value. +.. _reference-dic-type_guesser: + form.type_guesser ----------------- @@ -339,14 +341,13 @@ form.type_guesser This tag allows you to add your own logic to the :ref:`Form Guessing ` process. By default, form guessing is done by "guessers" based on the validation -metadata and Doctrine metadata (if you're using Doctrine). +metadata and Doctrine metadata (if you're using Doctrine) or Propel metadata +(if you're using Propel). -To add your own form type guesser, create a class that implements the -:class:`Symfony\\Component\\Form\\FormTypeGuesserInterface` interface. Next, -tag its service definition with ``form.type_guesser`` (it has no options). +.. seelalso:: -To see an example of how this class might look, see the ``ValidatorTypeGuesser`` -class in the Form component. + For information on how to create your own type guesser, see + :doc:`/components/form/type_guesser`. kernel.cache_clearer -------------------- From 0d37a3b320835e979b187d64679e5e183040a36e Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 9 Mar 2014 16:11:56 +0100 Subject: [PATCH 3/3] Applied comments --- components/form/type_guesser.rst | 122 ++++++++++++++++++------------- reference/forms/types/form.rst | 6 ++ 2 files changed, 78 insertions(+), 50 deletions(-) diff --git a/components/form/type_guesser.rst b/components/form/type_guesser.rst index 5cdacf77f4a..2cdc6c02dac 100644 --- a/components/form/type_guesser.rst +++ b/components/form/type_guesser.rst @@ -11,36 +11,42 @@ type guessers. .. sidebar:: Form Type Guessers in the Bridges - Symfony also provides some form type guessers in the bridges. These can be - used if you use that library. + 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. -A PHPDoc Type Guesser ---------------------- +Create a PHPDoc Type Guesser +---------------------------- -In this section, you are going to build a PHPDoc type guesser. At first, you -need to create a class which extends -:class:`Symfony\\Component\\Form\\FormTypeGuesserInterface`. This interface -requires 4 methods: +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 ``required`` option; + tries to guess the value of the :ref:`required ` + option; * :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessMaxLength` - - tries to guess the value of the ``max_length`` option; + tries to guess the value of the :ref:`max_length ` + option; * :method:`Symfony\\Component\\Form\\FormTypeGuesserInterface::guessPattern` - - tries to guess the value of the ``pattern`` option. + tries to guess the value of the :ref:`pattern ` + option. -The most basic class looks like:: +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 + class PhpdocTypeGuesser implements FormTypeGuesserInterface { public function guessType($class, $property) { @@ -69,54 +75,70 @@ that the type guesser cannot guess the type. The ``TypeGuess`` constructor requires 3 options: * The type name (one of the :doc:`form types readPhpDocAnnotations($class, $property); - - if (!isset($annotations['var'])) { - return; // guess nothing if the @var annotation is not available + 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); + } } - // 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; } } @@ -139,10 +161,10 @@ set. .. caution:: - You should be very careful with the ``guessPattern`` method. When the + 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 success). In + 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 @@ -164,6 +186,6 @@ The last thing you need to do is registering your custom type guesser by using .. note:: - When you use the full stack framework, you need to register your type - guesser and tag it with ``form.type_guesser``. For more information see + 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 `. diff --git a/reference/forms/types/form.rst b/reference/forms/types/form.rst index 70d78cb2788..eea2e872fd6 100644 --- a/reference/forms/types/form.rst +++ b/reference/forms/types/form.rst @@ -17,6 +17,8 @@ on all fields. .. include:: /reference/forms/types/options/empty_data.rst.inc +.. _reference-form-option-required: + .. include:: /reference/forms/types/options/required.rst.inc .. include:: /reference/forms/types/options/label.rst.inc @@ -43,6 +45,8 @@ on all fields. .. include:: /reference/forms/types/options/block_name.rst.inc +.. _reference-form-option-max_length: + .. include:: /reference/forms/types/options/max_length.rst.inc .. include:: /reference/forms/types/options/by_reference.rst.inc @@ -61,4 +65,6 @@ on all fields. .. include:: /reference/forms/types/options/post_max_size_message.rst.inc +.. _reference-form-option-pattern: + .. include:: /reference/forms/types/options/pattern.rst.inc