Skip to content

Commit 13cae15

Browse files
committed
Tweaks thanks to everyone!
1 parent 5bccea8 commit 13cae15

File tree

2 files changed

+129
-40
lines changed

2 files changed

+129
-40
lines changed

cookbook/doctrine/registration_form.rst

Lines changed: 127 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,37 @@ How to Implement a simple Registration Form
66
===========================================
77

88
Creating a registration form is pretty easy - it *really* means just creating
9-
a form that will update some ``User`` object (a Doctrine entity in this example)
9+
a form that will update some ``User`` model object (a Doctrine entity in this example)
1010
and then save it.
1111

12+
.. tip::
13+
14+
The popular `FOSUserBundle`_ provides a registration form, reset password form
15+
and other user management functionality.
16+
1217
If you don't already have a ``User`` entity and a working login system,
1318
first start with :doc:`/cookbook/security/entity_provider`.
1419

1520
Your ``User`` entity will probably at least have the following fields:
16-
* ``email``
17-
* ``username``
18-
* ``password`` (the encoded password)
19-
* ``plainPassword`` (*not* persisted: notice no ``@ORM\Column`` above it)
20-
* anything else you want
2121

22-
With some validation added, it may look something like this::
22+
``username``
23+
This will be used for logging in, unless you instead want your user to
24+
:ref:`login via email <registration-form-via-email>` (in that case, this
25+
field is unnecessary).
26+
27+
``email``
28+
A nice piece of information to collect. You can also allow users to
29+
:ref:`login via email <registration-form-via-email>`.
30+
31+
* ``password``
32+
The encoded password.
33+
34+
* ``plainPassword``
35+
This field is *not* persisted: (notice no ``@ORM\Column`` above it). It
36+
temporarily stores the plain password from the registration form. This field
37+
can be validated then used to populate the ``password`` field.
38+
39+
With some validation added, your class may look something like this::
2340

2441
// src/AppBundle/Entity/User.php
2542
namespace AppBundle\Entity;
@@ -41,41 +58,36 @@ With some validation added, it may look something like this::
4158
* @ORM\Column(type="integer")
4259
* @ORM\GeneratedValue(strategy="AUTO")
4360
*/
44-
protected $id;
61+
private $id;
4562

4663
/**
4764
* @ORM\Column(type="string", length=255)
4865
* @Assert\NotBlank()
4966
* @Assert\Email()
5067
*/
51-
protected $email;
68+
private $email;
5269

5370
/**
5471
* @ORM\Column(type="string", length=255)
5572
* @Assert\NotBlank()
5673
*/
57-
protected $username;
74+
private $username;
5875

5976
/**
6077
* @Assert\NotBlank()
6178
* @Assert\Length(max = 4096)
6279
*/
63-
protected $plainPassword;
80+
private $plainPassword;
6481

6582
/**
6683
* The below length depends on the "algorithm" you use for encoding
6784
* the password, but this works well with bcrypt
6885
*
6986
* @ORM\Column(type="string", length=64)
7087
*/
71-
protected $password;
88+
private $password;
7289

73-
// other properties
74-
75-
public function getId()
76-
{
77-
return $this->id;
78-
}
90+
// other properties and methods
7991

8092
public function getEmail()
8193
{
@@ -115,7 +127,9 @@ With some validation added, it may look something like this::
115127
// other methods, including security methods like getRoles()
116128
}
117129

118-
130+
The ``UserInterface`` requires a few other methods and your ``security.yml`` file
131+
needs to be configured properly to work with the ``User`` entity. For a more full
132+
example, see the :ref:`Entity Provider <security-crete-user-entity>` article.
119133

120134
.. _cookbook-registration-password-max:
121135

@@ -152,7 +166,9 @@ Next, create the form for the ``User`` entity::
152166
->add('email', 'email');
153167
->add('username', 'text');
154168
->add('plainPassword', 'repeated', array(
155-
'type' => 'password',
169+
'type' => 'password',
170+
'first_options' => array('label' => 'Password'),
171+
'second_options' => array('label' => 'Repeat Password'),
156172
)
157173
);
158174
}
@@ -206,13 +222,19 @@ controller for displaying the registration form::
206222

207223
// 2) handle the submit (will only happen on POST)
208224
$form->handleRequest($request);
209-
if ($form->isValid()) {
210-
// save the User!
225+
if ($form->isValid() && $form->isSubmitted()) {
226+
// 3) Encode the password (you could also do this via Doctrine listener)
227+
$encoder = $this->get('security.encoder_factory')
228+
->getEncoder($user);
229+
$password = $encoder->encodePassword($user->getPlainPassword(), $user->getSalt());
230+
$user->setPassword($password);
231+
232+
// 4) save the User!
211233
$em = $this->getDoctrine()->getManager();
212234
$em->persist($user);
213235
$em->flush();
214236

215-
// do any other work - like send them an email, etc
237+
// ... do any other work - like send them an email, etc
216238
// maybe set a "flash" success message for the user
217239

218240
$redirectUrl = $this->generateUrl('replace_with_some_route');
@@ -269,44 +291,109 @@ controller for displaying the registration form::
269291
270292
Next, create the template:
271293

272-
.. code-block:: html+jinja
294+
.. configuration-block::
273295

274-
{# app/Resources/views/registration/register.html.twig #}
296+
.. code-block:: html+jinja
297+
298+
{# app/Resources/views/registration/register.html.twig #}
299+
300+
{{ form_start(form) }}
301+
{{ form_row('form.username') }}
302+
{{ form_row('form.email') }}
303+
{{ form_row('form.plainPassword.first') }}
304+
{{ form_row('form.plainPassword.second') }}
305+
306+
<button type="submit">Register!</button>
307+
{{ form_end(form) }}
275308

276-
{{ form_start(form) }}
277-
{{ form_row('form.username') }}
278-
{{ form_row('form.email') }}
309+
.. code-block:: html+php
310+
311+
<!-- app/Resources/views/registration/register.html.php -->
279312

280-
{{ form_row('form.plainPassword.first', {
281-
'label': 'Password'
282-
}) }}
283-
{{ form_row('form.plainPassword.second', {
284-
'label': 'Repeat Password'
285-
}) }}
313+
<?php echo $view['form']->start($form) ?>
314+
<?php echo $view['form']->row($form['username']) ?>
315+
<?php echo $view['form']->row($form['email']) ?>
286316

287-
<button type="submit">Register!</button>
288-
{{ form_end(form) }}
317+
<?php echo $view['form']->row($form['plainPassword']['first']) ?>
318+
<?php echo $view['form']->row($form['plainPassword']['second']) ?>
319+
320+
<button type="submit">Register!</button>
321+
<?php echo $view['form']->end($form) ?>
322+
323+
See :doc:`/cookbook/form/form_customization` for more details.
289324

290325
Update your Database Schema
291326
---------------------------
292327

293-
If you've updated the ``User`` entity during this tutorial, make sure that
294-
your database schema has been updated properly:
328+
If you've updated the User entity during this tutorial, you have to update your
329+
database schema using this command:
295330

296331
.. code-block:: bash
297332
298333
$ php app/console doctrine:schema:update --force
299334
300335
That's it! Head to ``/register`` to try things out!
301336

337+
.. _registration-form-via-email:
338+
302339
Having a Registration form with only Email (no Username)
303340
--------------------------------------------------------
304341

305-
Todo
342+
If you want your users to login via email and you don't need a username, then you
343+
can remove it from your ``User`` entity entirely. Instead, make ``getUsername()``
344+
return the ``email`` property.
345+
346+
// src/AppBundle/Entity/User.php
347+
// ...
348+
349+
class User implements UserInterface
350+
{
351+
// ...
352+
353+
public function getUsername()
354+
{
355+
return $this->email;
356+
}
357+
358+
// ...
359+
}
360+
361+
Next, just update the ``providers`` section of your ``security.yml`` so that Symfony
362+
knows to load your users via the ``email`` property on login. See
363+
:ref:`authenticating-someone-with-a-custom-entity-provider`.
306364

307365
Adding a "accept terms" Checkbox
308366
--------------------------------
309367

310-
Todo
368+
Sometimes, you want a "Do you accept the terms and conditions" checkbox on your
369+
registration form. The only trick is that you want to add this field to your form
370+
without adding an unnecessary new ``termsAccepted`` property to your ``User`` entity
371+
that you'll never need.
372+
373+
To do this, add a ``termsAccepted`` field to your form, but set its :ref:`mapped <reference-form-option-mapped>`
374+
option to ``false``::
375+
376+
// src/AppBundle/Form/UserType.php
377+
// ...
378+
use Symfony\\Component\\Validator\\Constraints\\IsTrue;
379+
380+
class UserType extends AbstractType
381+
{
382+
public function buildForm(FormBuilderInterface $builder, array $options)
383+
{
384+
$builder
385+
->add('email', 'email');
386+
// ...
387+
->add('termsAccepted', 'checkbox', array(
388+
'mapped' => false,
389+
'constraints' => new IsTrue(),
390+
))
391+
);
392+
}
393+
}
394+
395+
The :ref:`constraints <form-option-constraints>` option is also used, which allows
396+
us to add validation, even though there is no ``termsAccepted`` property on ``User``.
311397

312398
.. _`CVE-2013-5750`: https://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form
399+
.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle

reference/forms/types/form.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ The actual default value of this option depends on other field options:
9595

9696
.. include:: /reference/forms/types/options/label_attr.rst.inc
9797

98+
.. _reference-form-option-mapped:
99+
98100
.. include:: /reference/forms/types/options/mapped.rst.inc
99101

100102
.. _reference-form-option-max_length:

0 commit comments

Comments
 (0)