From 67b8ff28f0746f5ff441cae1c588253f92ee6138 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 13 Oct 2015 21:16:03 -0400 Subject: [PATCH 01/13] Tweaks - see #5314 --- components/config/definition.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/config/definition.rst b/components/config/definition.rst index 1cfd366d986..0a769fd1fc3 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -212,6 +212,9 @@ Before defining the children of an array node, you can provide options like: ``addDefaultsIfNotSet()`` If any child nodes have default values, use them if explicit values haven't been provided. +``normalizeKeys(false)`` + If called (with ``false``), keys with dashes are *not* normaled to underscores. + It is recommended to use this to avoid this unnecessary normalization. A basic prototyped array configuration can be defined as follows:: @@ -310,6 +313,12 @@ The output configuration will be exactly the same as before. In other words, the ``sf_connection`` and ``default`` configuration keys are lost. The reason is that the Symfony Config component treats arrays as lists by default. +.. note:: + + As of writing this, there is an inconsistency: if only one file is processed, + the keys (i.e. ``sf_connection`` and ``default``) are *not* lost. But if more + than one file is processed, the keys are lost as described above. + In order to maintain the array keys use the ``useAttributeAsKey()`` method:: $node From 4e788e4204ea7cc0fad2e7d2185320bdaa8a131d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 13 Oct 2015 21:33:23 -0400 Subject: [PATCH 02/13] fixing build failure --- cookbook/event_dispatcher/event_listener.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/event_dispatcher/event_listener.rst b/cookbook/event_dispatcher/event_listener.rst index 3d50183110d..7ff3e3b2d2c 100644 --- a/cookbook/event_dispatcher/event_listener.rst +++ b/cookbook/event_dispatcher/event_listener.rst @@ -223,7 +223,7 @@ Request Events, Checking Types A single page can make several requests (one master request, and then multiple sub-requests - typically by :ref:`templating-embedding-controller`). For the core Symfony events, you might need to check to see if the event is for a "master" request -or a "sub request": +or a "sub request":: // src/AppBundle/EventListener/RequestListener.php namespace AppBundle\EventListener; From 51bc906ba6ea4b7db9ee6765944ac3e9fdef81e1 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 14 Oct 2015 20:49:50 -0400 Subject: [PATCH 03/13] Fixes thanks to Stof --- components/config/definition.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/components/config/definition.rst b/components/config/definition.rst index 0a769fd1fc3..9c69b9319bf 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -213,8 +213,9 @@ Before defining the children of an array node, you can provide options like: If any child nodes have default values, use them if explicit values haven't been provided. ``normalizeKeys(false)`` - If called (with ``false``), keys with dashes are *not* normaled to underscores. - It is recommended to use this to avoid this unnecessary normalization. + If called (with ``false``), keys with dashes are *not* normalized to underscores. + It is recommended to use this with prototype nodes where the user will define + a key-value map, to avoid an unnecessary transformation. A basic prototyped array configuration can be defined as follows:: @@ -315,9 +316,10 @@ the Symfony Config component treats arrays as lists by default. .. note:: - As of writing this, there is an inconsistency: if only one file is processed, - the keys (i.e. ``sf_connection`` and ``default``) are *not* lost. But if more - than one file is processed, the keys are lost as described above. + As of writing this, there is an inconsistency: if only one file provides the + configuration in question, the keys (i.e. ``sf_connection`` and ``default``) + are *not* lost. But if more than one file provides the configuration, the keys + are lost as described above. In order to maintain the array keys use the ``useAttributeAsKey()`` method:: From 639be4b589aed15c37f742d602513c4545101b1c Mon Sep 17 00:00:00 2001 From: Tim Stamp Date: Tue, 8 Sep 2015 14:01:30 +0100 Subject: [PATCH 04/13] Renaming constraint rst files to Is* to preserve edit history --- reference/constraints.rst | 6 +++--- reference/constraints/{False.rst => IsFalse.rst} | 5 +++++ reference/constraints/{Null.rst => IsNull.rst} | 5 +++++ reference/constraints/{True.rst => IsTrue.rst} | 5 +++++ 4 files changed, 18 insertions(+), 3 deletions(-) rename reference/constraints/{False.rst => IsFalse.rst} (96%) rename reference/constraints/{Null.rst => IsNull.rst} (95%) rename reference/constraints/{True.rst => IsTrue.rst} (96%) diff --git a/reference/constraints.rst b/reference/constraints.rst index 5d7e75c3d2b..b7ea1474a7a 100644 --- a/reference/constraints.rst +++ b/reference/constraints.rst @@ -8,9 +8,9 @@ Validation Constraints Reference constraints/NotBlank constraints/Blank constraints/NotNull - constraints/Null - constraints/True - constraints/False + constraints/IsNull + constraints/IsTrue + constraints/IsFalse constraints/Type constraints/Email diff --git a/reference/constraints/False.rst b/reference/constraints/IsFalse.rst similarity index 96% rename from reference/constraints/False.rst rename to reference/constraints/IsFalse.rst index 26984384390..b70d06b833e 100644 --- a/reference/constraints/False.rst +++ b/reference/constraints/IsFalse.rst @@ -1,6 +1,11 @@ False ===== +.. caution:: + + The ``False`` constraint is deprecated since Symfony 2.7 + and will be removed in Symfony 3.0. Use the ``IsFalse`` constraint instead. + Validates that a value is ``false``. Specifically, this checks to see if the value is exactly ``false``, exactly the integer ``0``, or exactly the string "``0``". diff --git a/reference/constraints/Null.rst b/reference/constraints/IsNull.rst similarity index 95% rename from reference/constraints/Null.rst rename to reference/constraints/IsNull.rst index 24f069732ce..97501c8bc05 100644 --- a/reference/constraints/Null.rst +++ b/reference/constraints/IsNull.rst @@ -1,6 +1,11 @@ Null ==== +.. caution:: + + The ``Null`` constraint is deprecated since Symfony 2.7 + and will be removed in Symfony 3.0. Use the ``IsNull`` constraint instead. + Validates that a value is exactly equal to ``null``. To force that a property is simply blank (blank string or ``null``), see the :doc:`/reference/constraints/Blank` constraint. To ensure that a property is not null, see :doc:`/reference/constraints/NotNull`. diff --git a/reference/constraints/True.rst b/reference/constraints/IsTrue.rst similarity index 96% rename from reference/constraints/True.rst rename to reference/constraints/IsTrue.rst index 0797795822e..477a827f317 100644 --- a/reference/constraints/True.rst +++ b/reference/constraints/IsTrue.rst @@ -1,6 +1,11 @@ True ==== +.. caution:: + + The ``True`` constraint is deprecated since Symfony 2.7 + and will be removed in Symfony 3.0. Use the ``IsTrue`` constraint instead. + Validates that a value is ``true``. Specifically, this checks to see if the value is exactly ``true``, exactly the integer ``1``, or exactly the string "``1``". From a90ea7c7c934a41260a3a527b01087df2066aeca Mon Sep 17 00:00:00 2001 From: Tim Stamp Date: Tue, 8 Sep 2015 14:05:06 +0100 Subject: [PATCH 05/13] Creating placeholder constraint rst docs for deprecated usage --- reference/constraints/False.rst | 9 +++++++++ reference/constraints/Null.rst | 9 +++++++++ reference/constraints/True.rst | 9 +++++++++ 3 files changed, 27 insertions(+) create mode 100644 reference/constraints/False.rst create mode 100644 reference/constraints/Null.rst create mode 100644 reference/constraints/True.rst diff --git a/reference/constraints/False.rst b/reference/constraints/False.rst new file mode 100644 index 00000000000..41bee6ee926 --- /dev/null +++ b/reference/constraints/False.rst @@ -0,0 +1,9 @@ +False +===== + +.. caution:: + + The ``False`` constraint is deprecated since Symfony 2.7 + and will be removed in Symfony 3.0. Use the ``IsFalse`` constraint instead. + +.. include:: /reference/constraints/IsFalse.rst diff --git a/reference/constraints/Null.rst b/reference/constraints/Null.rst new file mode 100644 index 00000000000..f6328fb14dd --- /dev/null +++ b/reference/constraints/Null.rst @@ -0,0 +1,9 @@ +Null +==== + +.. caution:: + + The ``Null`` constraint is deprecated since Symfony 2.7 + and will be removed in Symfony 3.0. Use the ``IsNull`` constraint instead. + +.. include:: /reference/constraints/IsNull.rst diff --git a/reference/constraints/True.rst b/reference/constraints/True.rst new file mode 100644 index 00000000000..285cd68bf15 --- /dev/null +++ b/reference/constraints/True.rst @@ -0,0 +1,9 @@ +True +==== + +.. caution:: + + The ``True`` constraint is deprecated since Symfony 2.7 + and will be removed in Symfony 3.0. Use the ``IsTrue`` constraint instead. + +.. include:: /reference/constraints/IsTrue.rst From 627d74d02d363e8025492a04f0a63a425332b389 Mon Sep 17 00:00:00 2001 From: Tim Stamp Date: Tue, 8 Sep 2015 14:17:21 +0100 Subject: [PATCH 06/13] Updated constraint reference docs for Null->IsNull, False->IsFalse, True->IsTrue --- reference/constraints/Blank.rst | 2 +- reference/constraints/IsFalse.rst | 44 ++++++++++++------------------- reference/constraints/IsNull.rst | 28 +++++++------------- reference/constraints/IsTrue.rst | 28 +++++++------------- 4 files changed, 37 insertions(+), 65 deletions(-) diff --git a/reference/constraints/Blank.rst b/reference/constraints/Blank.rst index 4b5ba5057ff..b46e9f508c7 100644 --- a/reference/constraints/Blank.rst +++ b/reference/constraints/Blank.rst @@ -3,7 +3,7 @@ Blank Validates that a value is blank, defined as equal to a blank string or equal to ``null``. To force that a value strictly be equal to ``null``, see the -:doc:`/reference/constraints/Null` constraint. To force that a value is +:doc:`/reference/constraints/IsNull` constraint. To force that a value is *not* blank, see :doc:`/reference/constraints/NotBlank`. +----------------+---------------------------------------------------------------------+ diff --git a/reference/constraints/IsFalse.rst b/reference/constraints/IsFalse.rst index b70d06b833e..92eb02fa934 100644 --- a/reference/constraints/IsFalse.rst +++ b/reference/constraints/IsFalse.rst @@ -1,31 +1,26 @@ -False -===== - -.. caution:: - - The ``False`` constraint is deprecated since Symfony 2.7 - and will be removed in Symfony 3.0. Use the ``IsFalse`` constraint instead. +IsFalse +======= Validates that a value is ``false``. Specifically, this checks to see if the value is exactly ``false``, exactly the integer ``0``, or exactly the string "``0``". -Also see :doc:`True `. +Also see :doc:`IsTrue `. -+----------------+---------------------------------------------------------------------+ -| Applies to | :ref:`property or method ` | -+----------------+---------------------------------------------------------------------+ -| Options | - `message`_ | -+----------------+---------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\False` | -+----------------+---------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\FalseValidator` | -+----------------+---------------------------------------------------------------------+ ++----------------+-----------------------------------------------------------------------+ +| Applies to | :ref:`property or method ` | ++----------------+-----------------------------------------------------------------------+ +| Options | - `message`_ | ++----------------+-----------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\IsFalse` | ++----------------+-----------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\IsFalseValidator` | ++----------------+-----------------------------------------------------------------------+ Basic Usage ----------- -The ``False`` constraint can be applied to a property or a "getter" method, +The ``IsFalse`` constraint can be applied to a property or a "getter" method, but is most commonly useful in the latter case. For example, suppose that you want to guarantee that some ``state`` property is *not* in a dynamic ``invalidStates`` array. First, you'd create a "getter" method:: @@ -54,7 +49,7 @@ method returns **false**: class Author { /** - * @Assert\False( + * @Assert\IsFalse( * message = "You've entered an invalid state." * ) */ @@ -70,7 +65,7 @@ method returns **false**: AppBundle\Entity\Author getters: stateInvalid: - - 'False': + - 'IsFalse': message: You've entered an invalid state. .. code-block:: xml @@ -83,7 +78,7 @@ method returns **false**: - + @@ -102,15 +97,10 @@ method returns **false**: { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addGetterConstraint('stateInvalid', new Assert\False()); + $metadata->addGetterConstraint('stateInvalid', new Assert\IsFalse()); } } -.. caution:: - - When using YAML, be sure to surround ``False`` with quotes (``'False'``) - or else YAML will convert this into a ``false`` boolean value. - Options ------- diff --git a/reference/constraints/IsNull.rst b/reference/constraints/IsNull.rst index 97501c8bc05..24629e9700d 100644 --- a/reference/constraints/IsNull.rst +++ b/reference/constraints/IsNull.rst @@ -1,23 +1,20 @@ -Null -==== - -.. caution:: - - The ``Null`` constraint is deprecated since Symfony 2.7 - and will be removed in Symfony 3.0. Use the ``IsNull`` constraint instead. +IsNull +====== Validates that a value is exactly equal to ``null``. To force that a property is simply blank (blank string or ``null``), see the :doc:`/reference/constraints/Blank` constraint. To ensure that a property is not null, see :doc:`/reference/constraints/NotNull`. +Also see :doc:`NotNull `. + +----------------+-----------------------------------------------------------------------+ | Applies to | :ref:`property or method ` | +----------------+-----------------------------------------------------------------------+ | Options | - `message`_ | +----------------+-----------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Null` | +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\IsNull` | +----------------+-----------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\NullValidator` | +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\IsNullValidator` | +----------------+-----------------------------------------------------------------------+ Basic Usage @@ -38,7 +35,7 @@ of an ``Author`` class exactly equal to ``null``, you could do the following: class Author { /** - * @Assert\Null() + * @Assert\IsNull() */ protected $firstName; } @@ -49,7 +46,7 @@ of an ``Author`` class exactly equal to ``null``, you could do the following: AppBundle\Entity\Author: properties: firstName: - - 'Null': ~ + - 'IsNull': ~ .. code-block:: xml @@ -61,7 +58,7 @@ of an ``Author`` class exactly equal to ``null``, you could do the following: - + @@ -78,15 +75,10 @@ of an ``Author`` class exactly equal to ``null``, you could do the following: { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('firstName', Assert\Null()); + $metadata->addPropertyConstraint('firstName', Assert\IsNull()); } } -.. caution:: - - When using YAML, be sure to surround ``Null`` with quotes (``'Null'``) - or else YAML will convert this into a ``null`` value. - Options ------- diff --git a/reference/constraints/IsTrue.rst b/reference/constraints/IsTrue.rst index 477a827f317..dbbfd31ca97 100644 --- a/reference/constraints/IsTrue.rst +++ b/reference/constraints/IsTrue.rst @@ -1,16 +1,11 @@ -True -==== - -.. caution:: - - The ``True`` constraint is deprecated since Symfony 2.7 - and will be removed in Symfony 3.0. Use the ``IsTrue`` constraint instead. +IsTrue +====== Validates that a value is ``true``. Specifically, this checks to see if the value is exactly ``true``, exactly the integer ``1``, or exactly the string "``1``". -Also see :doc:`False `. +Also see :doc:`IsFalse `. +----------------+---------------------------------------------------------------------+ | Applies to | :ref:`property or method ` | @@ -43,7 +38,7 @@ For example, suppose you have the following method:: } } -Then you can constrain this method with ``True``. +Then you can constrain this method with ``IsTrue``. .. configuration-block:: @@ -59,7 +54,7 @@ Then you can constrain this method with ``True``. protected $token; /** - * @Assert\True(message = "The token is invalid") + * @Assert\IsTrue(message = "The token is invalid") */ public function isTokenValid() { @@ -73,7 +68,7 @@ Then you can constrain this method with ``True``. AppBundle\Entity\Author: getters: tokenValid: - - 'True': + - 'IsTrue': message: The token is invalid. .. code-block:: xml @@ -86,7 +81,7 @@ Then you can constrain this method with ``True``. - + @@ -99,7 +94,7 @@ Then you can constrain this method with ``True``. namespace AppBundle\Entity; use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints\True; + use Symfony\Component\Validator\Constraints\IsTrue; class Author { @@ -107,7 +102,7 @@ Then you can constrain this method with ``True``. public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addGetterConstraint('tokenValid', new True(array( + $metadata->addGetterConstraint('tokenValid', new IsTrue(array( 'message' => 'The token is invalid.', ))); } @@ -120,11 +115,6 @@ Then you can constrain this method with ``True``. If the ``isTokenValid()`` returns false, the validation will fail. -.. caution:: - - When using YAML, be sure to surround ``True`` with quotes (``'True'``) - or else YAML will convert this into a ``true`` boolean value. - Options ------- From 9a5ea5696dc75e984a7804d6f88f61aaf8d3fd72 Mon Sep 17 00:00:00 2001 From: Tim Stamp Date: Tue, 8 Sep 2015 14:19:04 +0100 Subject: [PATCH 07/13] Updating book examples to not use deprecated validation methods --- book/validation.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/validation.rst b/book/validation.rst index e06a848e27f..b9b3f276f33 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -625,7 +625,7 @@ this method must return ``true``: class Author { /** - * @Assert\True(message = "The password cannot match your first name") + * @Assert\IsTrue(message = "The password cannot match your first name") */ public function isPasswordLegal() { @@ -670,7 +670,7 @@ this method must return ``true``: { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addGetterConstraint('passwordLegal', new Assert\True(array( + $metadata->addGetterConstraint('passwordLegal', new Assert\IsTrue(array( 'message' => 'The password cannot match your first name', ))); } @@ -928,7 +928,7 @@ username and the password are different only if all other validation passes private $password; /** - * @Assert\True(message="The password cannot match your username", groups={"Strict"}) + * @Assert\IsTrue(message="The password cannot match your username", groups={"Strict"}) */ public function isPasswordLegal() { @@ -1002,7 +1002,7 @@ username and the password are different only if all other validation passes $metadata->addPropertyConstraint('username', new Assert\NotBlank()); $metadata->addPropertyConstraint('password', new Assert\NotBlank()); - $metadata->addGetterConstraint('passwordLegal', new Assert\True(array( + $metadata->addGetterConstraint('passwordLegal', new Assert\IsTrue(array( 'message' => 'The password cannot match your first name', 'groups' => array('Strict'), ))); From 8a409c91f93c57e41fc5793258ce95d3a2c5c36a Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 14 Oct 2015 21:01:59 -0400 Subject: [PATCH 08/13] changing includes to links --- reference/constraints/False.rst | 5 ++--- reference/constraints/Null.rst | 5 ++--- reference/constraints/True.rst | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/reference/constraints/False.rst b/reference/constraints/False.rst index 41bee6ee926..3a895f5ae3d 100644 --- a/reference/constraints/False.rst +++ b/reference/constraints/False.rst @@ -4,6 +4,5 @@ False .. caution:: The ``False`` constraint is deprecated since Symfony 2.7 - and will be removed in Symfony 3.0. Use the ``IsFalse`` constraint instead. - -.. include:: /reference/constraints/IsFalse.rst + and will be removed in Symfony 3.0. Use the + :doc:`/reference/constraints/IsFalse` constraint instead. diff --git a/reference/constraints/Null.rst b/reference/constraints/Null.rst index f6328fb14dd..fd07a456eec 100644 --- a/reference/constraints/Null.rst +++ b/reference/constraints/Null.rst @@ -4,6 +4,5 @@ Null .. caution:: The ``Null`` constraint is deprecated since Symfony 2.7 - and will be removed in Symfony 3.0. Use the ``IsNull`` constraint instead. - -.. include:: /reference/constraints/IsNull.rst + and will be removed in Symfony 3.0. Use the + :doc:`/reference/constraints/IsNull` constraint instead. diff --git a/reference/constraints/True.rst b/reference/constraints/True.rst index 285cd68bf15..ba54ecbd637 100644 --- a/reference/constraints/True.rst +++ b/reference/constraints/True.rst @@ -4,6 +4,5 @@ True .. caution:: The ``True`` constraint is deprecated since Symfony 2.7 - and will be removed in Symfony 3.0. Use the ``IsTrue`` constraint instead. - -.. include:: /reference/constraints/IsTrue.rst + and will be removed in Symfony 3.0. Use the + :doc:`/reference/constraints/IsTrue` constraint instead. From 9c555ae53b335e59f91f483060e8cb30fb50638f Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 14 Oct 2015 22:20:55 -0400 Subject: [PATCH 09/13] Fixing build problems and wrong links --- reference/constraints.rst | 3 +++ reference/constraints/map.rst.inc | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/reference/constraints.rst b/reference/constraints.rst index b7ea1474a7a..59cc6603c48 100644 --- a/reference/constraints.rst +++ b/reference/constraints.rst @@ -9,8 +9,11 @@ Validation Constraints Reference constraints/Blank constraints/NotNull constraints/IsNull + constraints/Null constraints/IsTrue + constraints/True constraints/IsFalse + constraints/False constraints/Type constraints/Email diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 84186b01d25..fefba07a30b 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -7,9 +7,9 @@ the value of properties or the return value of methods on your object. * :doc:`NotBlank ` * :doc:`Blank ` * :doc:`NotNull ` -* :doc:`Null ` -* :doc:`True ` -* :doc:`False ` +* :doc:`Null ` +* :doc:`True ` +* :doc:`False ` * :doc:`Type ` String Constraints From 1c1b92a9706e659b768b7ef12f986eb4feabc485 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 28 Apr 2015 07:40:24 -0700 Subject: [PATCH 10/13] [WIP] Reworking most of the registration form: 1) Showing the embedded Registration model is way too hard and unnecessary 2) Stopped persisted plainPassword (why did we do this?) 3) Used AppBundle approach 4) Annotation routing various other small things --- cookbook/doctrine/registration_form.rst | 347 ++++++++++-------------- 1 file changed, 146 insertions(+), 201 deletions(-) diff --git a/cookbook/doctrine/registration_form.rst b/cookbook/doctrine/registration_form.rst index 418c4f1a4e6..ac6f7899943 100644 --- a/cookbook/doctrine/registration_form.rst +++ b/cookbook/doctrine/registration_form.rst @@ -5,15 +5,21 @@ How to Implement a simple Registration Form =========================================== -Some forms have extra fields whose values don't need to be stored in the -database. For example, you may want to create a registration form with some -extra fields (like a "terms accepted" checkbox field) and embed the form -that actually stores the account information. +Creating a registration form is pretty easy - it *really* means just creating +a form that will update some ``User`` object (a Doctrine entity in this example) +and then save it. -The simple User Model ---------------------- +If you don't already have a ``User`` entity and a working login system, +first start with :doc:`/cookbook/security/entity_provider`. -You have a simple ``User`` entity mapped to the database:: +Your ``User`` entity will probably at least have the following fields: +* ``email`` +* ``username`` +* ``password`` (the encoded password) +* ``plainPassword`` (*not* persisted: notice no ``@ORM\Column`` above it) +* anything else you want + +With some validation added, it may look something like this:: // src/AppBundle/Entity/User.php namespace AppBundle\Entity; @@ -21,12 +27,14 @@ You have a simple ``User`` entity mapped to the database:: use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; + use Symfony\Component\Security\Core\User\UserInterface; /** * @ORM\Entity * @UniqueEntity(fields="email", message="Email already taken") + * @UniqueEntity(fields="username", message="Username already taken") */ - class User + class User implements UserInterface { /** * @ORM\Id @@ -44,11 +52,26 @@ You have a simple ``User`` entity mapped to the database:: /** * @ORM\Column(type="string", length=255) + * @Assert\NotBlank() + */ + protected $username; + + /** * @Assert\NotBlank() * @Assert\Length(max = 4096) */ protected $plainPassword; + /** + * The below length depends on the "algorithm" you use for encoding + * the password, but this works well with bcrypt + * + * @ORM\Column(type="string", length=64) + */ + protected $password; + + // other properties + public function getId() { return $this->id; @@ -64,6 +87,16 @@ You have a simple ``User`` entity mapped to the database:: $this->email = $email; } + public function getUsername() + { + return $this->username; + } + + public function setUsername($username) + { + $this->username = $username; + } + public function getPlainPassword() { return $this->plainPassword; @@ -73,18 +106,16 @@ You have a simple ``User`` entity mapped to the database:: { $this->plainPassword = $password; } - } -This ``User`` entity contains three fields and two of them (``email`` and -``plainPassword``) should display on the form. The email property must be unique -in the database, this is enforced by adding this validation at the top of -the class. + public function setPassword($password) + { + $this->password = $password; + } + + // other methods, including security methods like getRoles() + } -.. note:: - If you want to integrate this User within the security system, you need - to implement the :ref:`UserInterface ` of the - Security component. .. _cookbook-registration-password-max: @@ -104,10 +135,10 @@ the class. Create a Form for the Model --------------------------- -Next, create the form for the ``User`` model:: +Next, create the form for the ``User`` entity:: - // src/AppBundle/Form/Type/UserType.php - namespace AppBundle\Form\Type; + // src/AppBundle/Form/UserType.php + namespace AppBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -117,12 +148,13 @@ Next, create the form for the ``User`` model:: { public function buildForm(FormBuilderInterface $builder, array $options) { - $builder->add('email', 'email'); - $builder->add('plainPassword', 'repeated', array( - 'first_name' => 'password', - 'second_name' => 'confirm', - 'type' => 'password', - )); + $builder + ->add('email', 'email'); + ->add('username', 'text'); + ->add('plainPassword', 'repeated', array( + 'type' => 'password', + ) + ); } public function setDefaultOptions(OptionsResolverInterface $resolver) @@ -138,98 +170,13 @@ Next, create the form for the ``User`` model:: } } -There are just two fields: ``email`` and ``plainPassword`` (repeated to confirm -the entered password). The ``data_class`` option tells the form the name of the -underlying data class (i.e. your ``User`` entity). +There are just three fields: ``email``, ``username`` and ``plainPassword`` +(repeated to confirm the entered password). .. tip:: To explore more things about the Form component, read :doc:`/book/forms`. -Embedding the User Form into a Registration Form ------------------------------------------------- - -The form that you'll use for the registration page is not the same as the -form used to simply modify the ``User`` (i.e. ``UserType``). The registration -form will contain further fields like "accept the terms", whose value won't -be stored in the database. - -Start by creating a simple class which represents the "registration":: - - // src/AppBundle/Form/Model/Registration.php - namespace AppBundle\Form\Model; - - use Symfony\Component\Validator\Constraints as Assert; - - use AppBundle\Entity\User; - - class Registration - { - /** - * @Assert\Type(type="AppBundle\Entity\User") - * @Assert\Valid() - */ - protected $user; - - /** - * @Assert\NotBlank() - * @Assert\True() - */ - protected $termsAccepted; - - public function setUser(User $user) - { - $this->user = $user; - } - - public function getUser() - { - return $this->user; - } - - public function getTermsAccepted() - { - return $this->termsAccepted; - } - - public function setTermsAccepted($termsAccepted) - { - $this->termsAccepted = (bool) $termsAccepted; - } - } - -Next, create the form for this ``Registration`` model:: - - // src/AppBundle/Form/Type/RegistrationType.php - namespace AppBundle\Form\Type; - - use Symfony\Component\Form\AbstractType; - use Symfony\Component\Form\FormBuilderInterface; - - class RegistrationType extends AbstractType - { - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('user', new UserType()); - $builder->add( - 'terms', - 'checkbox', - array('property_path' => 'termsAccepted') - ); - $builder->add('Register', 'submit'); - } - - public function getName() - { - return 'registration'; - } - } - -You don't need to use a special method for embedding the ``UserType`` form. -A form is a field, too - so you can add this like any other field, with the -expectation that the ``Registration.user`` property will hold an instance -of the ``User`` class. - Handling the Form Submission ---------------------------- @@ -241,127 +188,125 @@ controller for displaying the registration form:: use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use AppBundle\Form\Type\RegistrationType; - use AppBundle\Form\Model\Registration; + use AppBundle\Form\UserType; + use AppBundle\Entity\User; + use Symfony\Component\HttpFoundation\Request; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - class AccountController extends Controller + class RegistrationController extends Controller { - public function registerAction() + /** + * @Route("/register", name="user_registration") + */ + public function registerAction(Request $request) { - $registration = new Registration(); - $form = $this->createForm(new RegistrationType(), $registration, array( - 'action' => $this->generateUrl('account_create'), - )); + // 1) build the form + $user = new User(); + $form = $this->createForm(new UserType(), $user); + + // 2) handle the submit (will only happen on POST) + $form->handleRequest($request); + if ($form->isValid()) { + // save the User! + $em = $this->getDoctrine()->getManager(); + $em->persist($user); + $em->flush(); + + // do any other work - like send them an email, etc + // maybe set a "flash" success message for the user + + $redirectUrl = $this->generateUrl('replace_with_some_route'); + + return $this->redirect($redirectUrl); + } return $this->render( - 'AppBundle:Account:register.html.twig', + 'registration/register.html.twig', array('form' => $form->createView()) ); } } -And its template: - -.. code-block:: html+jinja - - {# src/AppBundle/Resources/views/Account/register.html.twig #} - {{ form(form) }} - -Next, create the controller which handles the form submission. This performs -the validation and saves the data into the database:: - - use Symfony\Component\HttpFoundation\Request; - // ... - - public function createAction(Request $request) - { - $em = $this->getDoctrine()->getManager(); - - $form = $this->createForm(new RegistrationType(), new Registration()); - - $form->handleRequest($request); - - if ($form->isValid()) { - $registration = $form->getData(); - - $em->persist($registration->getUser()); - $em->flush(); - - return $this->redirect(...); - } +.. note:: - return $this->render( - 'AppBundle:Account:register.html.twig', - array('form' => $form->createView()) - ); - } + If you decide to NOT use annotation routing (shown above), then you'll + need to create a route to this controller: + + .. configuration-block:: -Add new Routes --------------- + .. code-block:: yaml -Next, update your routes. If you're placing your routes inside your bundle -(as shown here), don't forget to make sure that the routing file is being -:ref:`imported `. + # app/config/routing.yml + user_registration: + path: /register + defaults: { _controller: AppBundle:Registration:register } -.. configuration-block:: + .. code-block:: xml - .. code-block:: yaml + + + - # src/AppBundle/Resources/config/routing.yml - account_register: - path: /register - defaults: { _controller: AppBundle:Account:register } + + AppBundle:Registration:register + + - account_create: - path: /register/create - defaults: { _controller: AppBundle:Account:create } + .. code-block:: php - .. code-block:: xml + // app/config/routing.php + use Symfony\Component\Routing\RouteCollection; + use Symfony\Component\Routing\Route; - - - + $collection = new RouteCollection(); + $collection->add('user_registration', new Route('/register', array( + '_controller' => 'AppBundle:Registration:register', + ))); - - AppBundle:Account:register - + return $collection; - - AppBundle:Account:create - - +Next, create the template: - .. code-block:: php +.. code-block:: html+jinja - // src/AppBundle/Resources/config/routing.php - use Symfony\Component\Routing\RouteCollection; - use Symfony\Component\Routing\Route; + {# app/Resources/views/registration/register.html.twig #} + + {{ form_start(form) }} + {{ form_row('form.username') }} + {{ form_row('form.email') }} - $collection = new RouteCollection(); - $collection->add('account_register', new Route('/register', array( - '_controller' => 'AppBundle:Account:register', - ))); - $collection->add('account_create', new Route('/register/create', array( - '_controller' => 'AppBundle:Account:create', - ))); + {{ form_row('form.plainPassword.first', { + 'label': 'Password' + }) }} + {{ form_row('form.plainPassword.second', { + 'label': 'Repeat Password' + }) }} - return $collection; + + {{ form_end(form) }} Update your Database Schema --------------------------- -Of course, since you've added a ``User`` entity during this tutorial, make -sure that your database schema has been updated properly: +If you've updated the ``User`` entity during this tutorial, make sure that +your database schema has been updated properly: .. code-block:: bash $ php app/console doctrine:schema:update --force -That's it! Your form now validates, and allows you to save the ``User`` -object to the database. The extra ``terms`` checkbox on the ``Registration`` -model class is used during validation, but not actually used afterwards when -saving the User to the database. +That's it! Head to ``/register`` to try things out! + +Having a Registration form with only Email (no Username) +-------------------------------------------------------- + +Todo + +Adding a "accept terms" Checkbox +-------------------------------- + +Todo .. _`CVE-2013-5750`: https://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form From 8b7fc790e49713ee7b8d6c0b5d6e2000bb6ce636 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 14 Oct 2015 22:09:29 -0400 Subject: [PATCH 11/13] Tweaks thanks to everyone! --- cookbook/doctrine/registration_form.rst | 167 ++++++++++++++++++------ reference/forms/types/form.rst | 2 + 2 files changed, 129 insertions(+), 40 deletions(-) diff --git a/cookbook/doctrine/registration_form.rst b/cookbook/doctrine/registration_form.rst index ac6f7899943..03cc6d85e8f 100644 --- a/cookbook/doctrine/registration_form.rst +++ b/cookbook/doctrine/registration_form.rst @@ -6,20 +6,37 @@ How to Implement a simple Registration Form =========================================== Creating a registration form is pretty easy - it *really* means just creating -a form that will update some ``User`` object (a Doctrine entity in this example) +a form that will update some ``User`` model object (a Doctrine entity in this example) and then save it. +.. tip:: + + The popular `FOSUserBundle`_ provides a registration form, reset password form + and other user management functionality. + If you don't already have a ``User`` entity and a working login system, first start with :doc:`/cookbook/security/entity_provider`. Your ``User`` entity will probably at least have the following fields: -* ``email`` -* ``username`` -* ``password`` (the encoded password) -* ``plainPassword`` (*not* persisted: notice no ``@ORM\Column`` above it) -* anything else you want -With some validation added, it may look something like this:: +``username`` + This will be used for logging in, unless you instead want your user to + :ref:`login via email ` (in that case, this + field is unnecessary). + +``email`` + A nice piece of information to collect. You can also allow users to + :ref:`login via email `. + +* ``password`` + The encoded password. + +* ``plainPassword`` + This field is *not* persisted: (notice no ``@ORM\Column`` above it). It + temporarily stores the plain password from the registration form. This field + can be validated then used to populate the ``password`` field. + +With some validation added, your class may look something like this:: // src/AppBundle/Entity/User.php namespace AppBundle\Entity; @@ -41,26 +58,26 @@ With some validation added, it may look something like this:: * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ - protected $id; + private $id; /** * @ORM\Column(type="string", length=255) * @Assert\NotBlank() * @Assert\Email() */ - protected $email; + private $email; /** * @ORM\Column(type="string", length=255) * @Assert\NotBlank() */ - protected $username; + private $username; /** * @Assert\NotBlank() * @Assert\Length(max = 4096) */ - protected $plainPassword; + private $plainPassword; /** * The below length depends on the "algorithm" you use for encoding @@ -68,14 +85,9 @@ With some validation added, it may look something like this:: * * @ORM\Column(type="string", length=64) */ - protected $password; + private $password; - // other properties - - public function getId() - { - return $this->id; - } + // other properties and methods public function getEmail() { @@ -115,7 +127,9 @@ With some validation added, it may look something like this:: // other methods, including security methods like getRoles() } - +The ``UserInterface`` requires a few other methods and your ``security.yml`` file +needs to be configured properly to work with the ``User`` entity. For a more full +example, see the :ref:`Entity Provider ` article. .. _cookbook-registration-password-max: @@ -152,7 +166,9 @@ Next, create the form for the ``User`` entity:: ->add('email', 'email'); ->add('username', 'text'); ->add('plainPassword', 'repeated', array( - 'type' => 'password', + 'type' => 'password', + 'first_options' => array('label' => 'Password'), + 'second_options' => array('label' => 'Repeat Password'), ) ); } @@ -206,13 +222,19 @@ controller for displaying the registration form:: // 2) handle the submit (will only happen on POST) $form->handleRequest($request); - if ($form->isValid()) { - // save the User! + if ($form->isValid() && $form->isSubmitted()) { + // 3) Encode the password (you could also do this via Doctrine listener) + $encoder = $this->get('security.encoder_factory') + ->getEncoder($user); + $password = $encoder->encodePassword($user->getPlainPassword(), $user->getSalt()); + $user->setPassword($password); + + // 4) save the User! $em = $this->getDoctrine()->getManager(); $em->persist($user); $em->flush(); - // do any other work - like send them an email, etc + // ... do any other work - like send them an email, etc // maybe set a "flash" success message for the user $redirectUrl = $this->generateUrl('replace_with_some_route'); @@ -269,29 +291,42 @@ controller for displaying the registration form:: Next, create the template: -.. code-block:: html+jinja +.. configuration-block:: - {# app/Resources/views/registration/register.html.twig #} + .. code-block:: html+jinja + + {# app/Resources/views/registration/register.html.twig #} + + {{ form_start(form) }} + {{ form_row('form.username') }} + {{ form_row('form.email') }} + {{ form_row('form.plainPassword.first') }} + {{ form_row('form.plainPassword.second') }} + + + {{ form_end(form) }} - {{ form_start(form) }} - {{ form_row('form.username') }} - {{ form_row('form.email') }} + .. code-block:: html+php + + - {{ form_row('form.plainPassword.first', { - 'label': 'Password' - }) }} - {{ form_row('form.plainPassword.second', { - 'label': 'Repeat Password' - }) }} + start($form) ?> + row($form['username']) ?> + row($form['email']) ?> - - {{ form_end(form) }} + row($form['plainPassword']['first']) ?> + row($form['plainPassword']['second']) ?> + + + end($form) ?> + +See :doc:`/cookbook/form/form_customization` for more details. Update your Database Schema --------------------------- -If you've updated the ``User`` entity during this tutorial, make sure that -your database schema has been updated properly: +If you've updated the User entity during this tutorial, you have to update your +database schema using this command: .. code-block:: bash @@ -299,14 +334,66 @@ your database schema has been updated properly: That's it! Head to ``/register`` to try things out! +.. _registration-form-via-email: + Having a Registration form with only Email (no Username) -------------------------------------------------------- -Todo +If you want your users to login via email and you don't need a username, then you +can remove it from your ``User`` entity entirely. Instead, make ``getUsername()`` +return the ``email`` property. + + // src/AppBundle/Entity/User.php + // ... + + class User implements UserInterface + { + // ... + + public function getUsername() + { + return $this->email; + } + + // ... + } + +Next, just update the ``providers`` section of your ``security.yml`` so that Symfony +knows to load your users via the ``email`` property on login. See +:ref:`authenticating-someone-with-a-custom-entity-provider`. Adding a "accept terms" Checkbox -------------------------------- -Todo +Sometimes, you want a "Do you accept the terms and conditions" checkbox on your +registration form. The only trick is that you want to add this field to your form +without adding an unnecessary new ``termsAccepted`` property to your ``User`` entity +that you'll never need. + +To do this, add a ``termsAccepted`` field to your form, but set its :ref:`mapped ` +option to ``false``:: + + // src/AppBundle/Form/UserType.php + // ... + use Symfony\\Component\\Validator\\Constraints\\IsTrue; + + class UserType extends AbstractType + { + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('email', 'email'); + // ... + ->add('termsAccepted', 'checkbox', array( + 'mapped' => false, + 'constraints' => new IsTrue(), + )) + ); + } + } + +The :ref:`constraints ` option is also used, which allows +us to add validation, even though there is no ``termsAccepted`` property on ``User``. .. _`CVE-2013-5750`: https://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form +.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle diff --git a/reference/forms/types/form.rst b/reference/forms/types/form.rst index ac3b96d5a0c..1478fb0c8e0 100644 --- a/reference/forms/types/form.rst +++ b/reference/forms/types/form.rst @@ -95,6 +95,8 @@ The actual default value of this option depends on other field options: .. include:: /reference/forms/types/options/label_attr.rst.inc +.. _reference-form-option-mapped: + .. include:: /reference/forms/types/options/mapped.rst.inc .. _reference-form-option-max_length: From a8275a38bbfd0c6eddd52015c6cef1afeed1f5b1 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 14 Oct 2015 22:15:39 -0400 Subject: [PATCH 12/13] Fixing build error --- cookbook/doctrine/registration_form.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/doctrine/registration_form.rst b/cookbook/doctrine/registration_form.rst index 03cc6d85e8f..956026248c1 100644 --- a/cookbook/doctrine/registration_form.rst +++ b/cookbook/doctrine/registration_form.rst @@ -341,7 +341,7 @@ Having a Registration form with only Email (no Username) If you want your users to login via email and you don't need a username, then you can remove it from your ``User`` entity entirely. Instead, make ``getUsername()`` -return the ``email`` property. +return the ``email`` property:: // src/AppBundle/Entity/User.php // ... From b6e61ca547d22337829d387d638a395df82995f0 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 14 Oct 2015 22:28:32 -0400 Subject: [PATCH 13/13] tweaks for 2.7 --- cookbook/doctrine/registration_form.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cookbook/doctrine/registration_form.rst b/cookbook/doctrine/registration_form.rst index 1ee8500c903..f82d1509292 100644 --- a/cookbook/doctrine/registration_form.rst +++ b/cookbook/doctrine/registration_form.rst @@ -224,9 +224,8 @@ controller for displaying the registration form:: $form->handleRequest($request); if ($form->isValid() && $form->isSubmitted()) { // 3) Encode the password (you could also do this via Doctrine listener) - $encoder = $this->get('security.encoder_factory') - ->getEncoder($user); - $password = $encoder->encodePassword($user->getPlainPassword(), $user->getSalt()); + $password = $this->get('security.password_encoder') + ->encodePassword($user, $user->getPlainPassword()); $user->setPassword($password); // 4) save the User! @@ -237,9 +236,7 @@ controller for displaying the registration form:: // ... do any other work - like send them an email, etc // maybe set a "flash" success message for the user - $redirectUrl = $this->generateUrl('replace_with_some_route'); - - return $this->redirect($redirectUrl); + return $this->redirectToRoute('replace_with_some_route'); } return $this->render(