@@ -6,20 +6,37 @@ How to Implement a simple Registration Form
6
6
===========================================
7
7
8
8
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)
10
10
and then save it.
11
11
12
+ .. tip ::
13
+
14
+ The popular `FOSUserBundle `_ provides a registration form, reset password form
15
+ and other user management functionality.
16
+
12
17
If you don't already have a ``User `` entity and a working login system,
13
18
first start with :doc: `/cookbook/security/entity_provider `.
14
19
15
20
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
21
21
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::
23
40
24
41
// src/AppBundle/Entity/User.php
25
42
namespace AppBundle\Entity;
@@ -41,41 +58,36 @@ With some validation added, it may look something like this::
41
58
* @ORM\Column(type="integer")
42
59
* @ORM\GeneratedValue(strategy="AUTO")
43
60
*/
44
- protected $id;
61
+ private $id;
45
62
46
63
/**
47
64
* @ORM\Column(type="string", length=255)
48
65
* @Assert\NotBlank()
49
66
* @Assert\Email()
50
67
*/
51
- protected $email;
68
+ private $email;
52
69
53
70
/**
54
71
* @ORM\Column(type="string", length=255)
55
72
* @Assert\NotBlank()
56
73
*/
57
- protected $username;
74
+ private $username;
58
75
59
76
/**
60
77
* @Assert\NotBlank()
61
78
* @Assert\Length(max = 4096)
62
79
*/
63
- protected $plainPassword;
80
+ private $plainPassword;
64
81
65
82
/**
66
83
* The below length depends on the "algorithm" you use for encoding
67
84
* the password, but this works well with bcrypt
68
85
*
69
86
* @ORM\Column(type="string", length=64)
70
87
*/
71
- protected $password;
88
+ private $password;
72
89
73
- // other properties
74
-
75
- public function getId()
76
- {
77
- return $this->id;
78
- }
90
+ // other properties and methods
79
91
80
92
public function getEmail()
81
93
{
@@ -115,7 +127,9 @@ With some validation added, it may look something like this::
115
127
// other methods, including security methods like getRoles()
116
128
}
117
129
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.
119
133
120
134
.. _cookbook-registration-password-max :
121
135
@@ -152,7 +166,9 @@ Next, create the form for the ``User`` entity::
152
166
->add('email', 'email');
153
167
->add('username', 'text');
154
168
->add('plainPassword', 'repeated', array(
155
- 'type' => 'password',
169
+ 'type' => 'password',
170
+ 'first_options' => array('label' => 'Password'),
171
+ 'second_options' => array('label' => 'Repeat Password'),
156
172
)
157
173
);
158
174
}
@@ -206,13 +222,19 @@ controller for displaying the registration form::
206
222
207
223
// 2) handle the submit (will only happen on POST)
208
224
$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!
211
233
$em = $this->getDoctrine()->getManager();
212
234
$em->persist($user);
213
235
$em->flush();
214
236
215
- // do any other work - like send them an email, etc
237
+ // ... do any other work - like send them an email, etc
216
238
// maybe set a "flash" success message for the user
217
239
218
240
$redirectUrl = $this->generateUrl('replace_with_some_route');
@@ -269,44 +291,109 @@ controller for displaying the registration form::
269
291
270
292
Next, create the template:
271
293
272
- .. code -block :: html+jinja
294
+ .. configuration -block ::
273
295
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) }}
275
308
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 -->
279
312
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']) ?>
286
316
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.
289
324
290
325
Update your Database Schema
291
326
---------------------------
292
327
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 :
295
330
296
331
.. code-block :: bash
297
332
298
333
$ php app/console doctrine:schema:update --force
299
334
300
335
That's it! Head to ``/register `` to try things out!
301
336
337
+ .. _registration-form-via-email :
338
+
302
339
Having a Registration form with only Email (no Username)
303
340
--------------------------------------------------------
304
341
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 `.
306
364
307
365
Adding a "accept terms" Checkbox
308
366
--------------------------------
309
367
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 ``.
311
397
312
398
.. _`CVE-2013-5750` : https://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form
399
+ .. _`FOSUserBundle` : https://github.com/FriendsOfSymfony/FOSUserBundle
0 commit comments