1
1
.. index ::
2
2
single: Doctrine; Simple Registration Form
3
3
single: Form; Simple Registration Form
4
+ single: Security; Simple Registration Form
4
5
5
6
How to Implement a simple Registration Form
6
7
===========================================
@@ -10,13 +11,13 @@ database. For example, you may want to create a registration form with some
10
11
extra fields (like a "terms accepted" checkbox field) and embed the form
11
12
that actually stores the account information.
12
13
13
- The simple User Model
14
- ---------------------
14
+ The simple User Entity
15
+ ----------------------
15
16
16
17
You have a simple ``User `` entity mapped to the database::
17
18
18
- // src/Acme/AccountBundle /Entity/User.php
19
- namespace Acme\AccountBundle \Entity;
19
+ // src/AppBundle /Entity/User.php
20
+ namespace AppBundle \Entity;
20
21
21
22
use Doctrine\ORM\Mapping as ORM;
22
23
use Symfony\Component\Validator\Constraints as Assert;
@@ -36,7 +37,7 @@ You have a simple ``User`` entity mapped to the database::
36
37
protected $id;
37
38
38
39
/**
39
- * @ORM\Column(type="string", length=255)
40
+ * @ORM\Column(type="string", length=255, unique=true )
40
41
* @Assert\NotBlank()
41
42
* @Assert\Email()
42
43
*/
@@ -45,9 +46,9 @@ You have a simple ``User`` entity mapped to the database::
45
46
/**
46
47
* @ORM\Column(type="string", length=255)
47
48
* @Assert\NotBlank()
48
- * @Assert\Length(max = 4096)
49
+ * @Assert\Length(max= 4096)
49
50
*/
50
- protected $plainPassword ;
51
+ protected $password ;
51
52
52
53
public function getId()
53
54
{
@@ -64,21 +65,27 @@ You have a simple ``User`` entity mapped to the database::
64
65
$this->email = $email;
65
66
}
66
67
67
- public function getPlainPassword()
68
+ public function getPassword()
69
+ {
70
+ return $this->password;
71
+ }
72
+
73
+ public function setPassword($password)
68
74
{
69
- return $this->plainPassword ;
75
+ $this->password = $password ;
70
76
}
71
77
72
- public function setPlainPassword($password )
78
+ public function getSalt( )
73
79
{
74
- $this->plainPassword = $password ;
80
+ return null ;
75
81
}
76
82
}
77
83
78
84
This ``User `` entity contains three fields and two of them (``email `` and
79
- ``plainPassword ``) should display on the form. The email property must be unique
80
- in the database, this is enforced by adding this validation at the top of
81
- the class.
85
+ ``password ``) should be displayed by the form. The ``email `` property must
86
+ be unique in the database, this is enforced by adding an ``@UniqueEntity ``
87
+ validation constraint at the top of the class for application-side validation
88
+ and by adding ``unique=true `` to the column mapping for the database schema.
82
89
83
90
.. note ::
84
91
@@ -90,7 +97,7 @@ the class.
90
97
91
98
.. sidebar :: Why the 4096 Password Limit?
92
99
93
- Notice that the ``plainPassword `` field has a max length of 4096 characters.
100
+ Notice that the ``password `` field has a max length of 4096 characters.
94
101
For security purposes (`CVE-2013-5750 `_), Symfony limits the plain password
95
102
length to 4096 characters when encoding it. Adding this constraint makes
96
103
sure that your form will give a validation error if anyone tries a super-long
@@ -101,13 +108,13 @@ the class.
101
108
only place where you don't need to worry about this is your login form,
102
109
since Symfony's Security component handles this for you.
103
110
104
- Create a Form for the Model
105
- ---------------------------
111
+ Create a Form for the Entity
112
+ ----------------------------
106
113
107
- Next, create the form for the ``User `` model ::
114
+ Next, create the form for the ``User `` entity ::
108
115
109
- // src/Acme/AccountBundle /Form/Type/UserType.php
110
- namespace Acme\AccountBundle \Form\Type;
116
+ // src/AppBundle /Form/Type/UserType.php
117
+ namespace AppBundle \Form\Type;
111
118
112
119
use Symfony\Component\Form\AbstractType;
113
120
use Symfony\Component\Form\FormBuilderInterface;
@@ -118,17 +125,17 @@ Next, create the form for the ``User`` model::
118
125
public function buildForm(FormBuilderInterface $builder, array $options)
119
126
{
120
127
$builder->add('email', 'email');
121
- $builder->add('plainPassword ', 'repeated', array(
122
- 'first_name' => 'password',
123
- 'second_name' => 'confirm',
124
- 'type' => 'password',
128
+ $builder->add('password ', 'repeated', array(
129
+ 'first_name' => 'password',
130
+ 'second_name' => 'confirm',
131
+ 'type' => 'password',
125
132
));
126
133
}
127
134
128
135
public function setDefaultOptions(OptionsResolverInterface $resolver)
129
136
{
130
137
$resolver->setDefaults(array(
131
- 'data_class' => 'Acme\AccountBundle \Entity\User'
138
+ 'data_class' => 'AppBundle \Entity\User'
132
139
));
133
140
}
134
141
@@ -138,7 +145,7 @@ Next, create the form for the ``User`` model::
138
145
}
139
146
}
140
147
141
- There are just two fields: ``email `` and ``plainPassword `` (repeated to confirm
148
+ There are just two fields: ``email `` and ``password `` (repeated to confirm
142
149
the entered password). The ``data_class `` option tells the form the name of the
143
150
underlying data class (i.e. your ``User `` entity).
144
151
@@ -156,17 +163,17 @@ be stored in the database.
156
163
157
164
Start by creating a simple class which represents the "registration"::
158
165
159
- // src/Acme/AccountBundle /Form/Model/Registration.php
160
- namespace Acme\AccountBundle \Form\Model;
166
+ // src/AppBundle /Form/Model/Registration.php
167
+ namespace AppBundle \Form\Model;
161
168
162
169
use Symfony\Component\Validator\Constraints as Assert;
163
170
164
- use Acme\AccountBundle \Entity\User;
171
+ use AppBundle \Entity\User;
165
172
166
173
class Registration
167
174
{
168
175
/**
169
- * @Assert\Type(type="Acme\AccountBundle \Entity\User")
176
+ * @Assert\Type(type="AppBundle \Entity\User")
170
177
* @Assert\Valid()
171
178
*/
172
179
protected $user;
@@ -200,8 +207,8 @@ Start by creating a simple class which represents the "registration"::
200
207
201
208
Next, create the form for this ``Registration `` model::
202
209
203
- // src/Acme/AccountBundle /Form/Type/RegistrationType.php
204
- namespace Acme\AccountBundle \Form\Type;
210
+ // src/AppBundle /Form/Type/RegistrationType.php
211
+ namespace AppBundle \Form\Type;
205
212
206
213
use Symfony\Component\Form\AbstractType;
207
214
use Symfony\Component\Form\FormBuilderInterface;
@@ -211,11 +218,9 @@ Next, create the form for this ``Registration`` model::
211
218
public function buildForm(FormBuilderInterface $builder, array $options)
212
219
{
213
220
$builder->add('user', new UserType());
214
- $builder->add(
215
- 'terms',
216
- 'checkbox',
217
- array('property_path' => 'termsAccepted')
218
- );
221
+ $builder->add('termsAccepted', 'checkbox', array(
222
+ 'label' => 'Terms accepted',
223
+ ));
219
224
$builder->add('Register', 'submit');
220
225
}
221
226
@@ -233,121 +238,93 @@ of the ``User`` class.
233
238
Handling the Form Submission
234
239
----------------------------
235
240
236
- Next, you need a controller to handle the form. Start by creating a simple
237
- controller for displaying the registration form::
241
+ Next, you need a controller to handle the form rendering and submission. If the
242
+ form is submitted, the controller performs the validation and saves the data
243
+ into the database::
238
244
239
- // src/Acme/AccountBundle /Controller/AccountController.php
240
- namespace Acme\AccountBundle \Controller;
245
+ // src/AppBundle /Controller/AccountController.php
246
+ namespace AppBundle \Controller;
241
247
242
248
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
249
+ use Symfony\Component\HttpFoundation\Request;
250
+ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
243
251
244
- use Acme\AccountBundle \Form\Type\RegistrationType;
245
- use Acme\AccountBundle \Form\Model\Registration;
252
+ use AppBundle \Form\Type\RegistrationType;
253
+ use AppBundle \Form\Model\Registration;
246
254
247
255
class AccountController extends Controller
248
256
{
249
- public function registerAction()
257
+ /**
258
+ * @Route("/register", name="account_register")
259
+ */
260
+ public function registerAction(Request $request)
250
261
{
251
- $registration = new Registration();
252
- $form = $this->createForm(new RegistrationType(), $registration, array(
253
- 'action' => $this->generateUrl('account_create'),
254
- ));
262
+ $form = $this->createForm(new RegistrationType(), new Registration());
255
263
256
- return $this->render(
257
- 'AcmeAccountBundle:Account:register.html.twig',
258
- array('form' => $form->createView())
259
- );
260
- }
261
- }
264
+ $form->handleRequest($request);
262
265
263
- And its template:
266
+ if ($form->isSubmitted() && $form->isValid()) {
267
+ $registration = $form->getData();
268
+ $user = $registration->getUser();
264
269
265
- .. code-block :: html+jinja
270
+ $password = $this
271
+ ->get('security.encoder_factory')
272
+ ->getEncoder($user)
273
+ ->encodePassword(
274
+ $user->getPassword(),
275
+ $user->getSalt()
276
+ );
277
+ $user->setPassword($password);
266
278
267
- {# src/Acme/AccountBundle/Resources/views/Account/register.html.twig #}
268
- {{ form(form) }}
279
+ $em = $this->getDoctrine()->getManager();
280
+ $em->persist($user);
281
+ $em->flush();
269
282
270
- Next, create the controller which handles the form submission. This performs
271
- the validation and saves the data into the database::
272
-
273
- use Symfony\Component\HttpFoundation\Request;
274
- // ...
283
+ return $this->redirect($this->generateUrl('homepage'));
284
+ }
275
285
276
- public function createAction(Request $request)
277
- {
278
- $em = $this->getDoctrine()->getManager();
279
-
280
- $form = $this->createForm(new RegistrationType(), new Registration());
281
-
282
- $form->handleRequest($request);
283
-
284
- if ($form->isValid()) {
285
- $registration = $form->getData();
286
-
287
- $em->persist($registration->getUser());
288
- $em->flush();
289
-
290
- return $this->redirect(...);
286
+ return $this->render(
287
+ 'account/register.html.twig',
288
+ array('form' => $form->createView())
289
+ );
291
290
}
292
-
293
- return $this->render(
294
- 'AcmeAccountBundle:Account:register.html.twig',
295
- array('form' => $form->createView())
296
- );
297
291
}
298
292
299
- Add new Routes
300
- --------------
301
-
302
- Next, update your routes. If you're placing your routes inside your bundle
303
- (as shown here), don't forget to make sure that the routing file is being
304
- :ref: `imported <routing-include-external-resources >`.
305
-
306
- .. configuration-block ::
293
+ Storing plain-text passwords is bad practice, so before saving the user
294
+ data into the database the submitted plain-text password is replaced by
295
+ an encoded one. To define the algorithm used to encode the password
296
+ configure the encoder in the security configuration:
307
297
308
- .. code-block :: yaml
298
+ .. code-block :: yaml
309
299
310
- # src/Acme/AccountBundle/Resources/ config/routing .yml
311
- account_register :
312
- path : /register
313
- defaults : { _controller: AcmeAccountBundle:Account:register }
300
+ # app/ config/security .yml
301
+ security :
302
+ encoders :
303
+ AppBundle\Entity\User : bcrypt
314
304
315
- account_create :
316
- path : /register/create
317
- defaults : { _controller: AcmeAccountBundle:Account:create }
305
+ In this case the recommended `` bcrypt `` algorithm is used. To learn more
306
+ about how to encode the users password have a look into the
307
+ :ref: ` security chapter < book-security-encoding-user-password >`
318
308
319
- .. code-block :: xml
320
-
321
- <!-- src/Acme/AccountBundle/Resources/config/routing.xml -->
322
- <?xml version =" 1.0" encoding =" UTF-8" ?>
323
- <routes xmlns =" http://symfony.com/schema/routing"
324
- xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
325
- xsi : schemaLocation =" http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd" >
309
+ .. note ::
326
310
327
- < route id = " account_register " path = " /register " >
328
- < default key = " _controller " >AcmeAccountBundle:Account:register</ default >
329
- </ route >
311
+ While `` $form->isSubmitted() `` isn't technically needed, since isValid()
312
+ first calls ` isSubmitted() `, it is recommended to use it to improve
313
+ readability.
330
314
331
- <route id =" account_create" path =" /register/create" >
332
- <default key =" _controller" >AcmeAccountBundle:Account:create</default >
333
- </route >
334
- </routes >
315
+ And its template:
335
316
336
- .. code-block :: php
317
+ .. code-block :: html+jinja
337
318
338
- // src/Acme/AccountBundle/Resources/config/routing.php
339
- use Symfony\Component\Routing\RouteCollection;
340
- use Symfony\Component\Routing\Route;
319
+ {# app/Resources/views/account/register.html.twig #}
320
+ {{ form(form) }}
341
321
342
- $collection = new RouteCollection();
343
- $collection->add('account_register', new Route('/register', array(
344
- '_controller' => 'AcmeAccountBundle:Account:register',
345
- )));
346
- $collection->add('account_create', new Route('/register/create', array(
347
- '_controller' => 'AcmeAccountBundle:Account:create',
348
- )));
322
+ Add new Routes
323
+ --------------
349
324
350
- return $collection;
325
+ Don't forget to make sure that the routes defined as annotations in your
326
+ controller are :ref: `loaded <routing-include-external-resources >` by your main
327
+ routing configuration file.
351
328
352
329
Update your Database Schema
353
330
---------------------------
@@ -360,8 +337,8 @@ sure that your database schema has been updated properly:
360
337
$ php app/console doctrine:schema:update --force
361
338
362
339
That's it! Your form now validates, and allows you to save the ``User ``
363
- object to the database. The extra ``terms `` checkbox on the `` Registration ``
364
- model class is used during validation, but not actually used afterwards when
365
- saving the User to the database.
340
+ object to the database. The extra ``termsAccepted `` checkbox on the
341
+ `` Registration `` model class is used during validation, but not actually used
342
+ afterwards when saving the User to the database.
366
343
367
344
.. _`CVE-2013-5750` : http://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form
0 commit comments