Skip to content

Commit 1d00f06

Browse files
gondoweaverryan
authored andcommitted
Fixing Security Entity Provider tutorial
for more info see symfony#2756
1 parent 0849175 commit 1d00f06

File tree

2 files changed

+91
-70
lines changed

2 files changed

+91
-70
lines changed

book/doctrine.rst

+4
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,8 @@ see the :ref:`book-doctrine-field-types` section.
364364
class Product
365365
// ...
366366

367+
.. _book-doctrine-generating-getters-and-setters:
368+
367369
Generating Getters and Setters
368370
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
369371

@@ -426,6 +428,8 @@ mapping information) of a bundle or an entire namespace:
426428
The getters and setters are generated here only because you'll need them
427429
to interact with your PHP object.
428430

431+
.. _book-doctrine-creating-the-database-tables-schema:
432+
429433
Creating the Database Tables/Schema
430434
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
431435

cookbook/security/entity_provider.rst

+87-70
Original file line numberDiff line numberDiff line change
@@ -197,24 +197,32 @@ For more details on each of these, see :class:`Symfony\\Component\\Security\\Cor
197197
because the :method:`Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider::refreshUser`
198198
method reloads the user on each request by using the ``id``.
199199

200-
Below is an export of my ``User`` table from MySQL. For details on how to
201-
create user records and encode their password, see :ref:`book-security-encoding-user-password`.
200+
.. tip::
201+
202+
To generate missing setters and getters for your ``User`` entity, you
203+
can use ``php app/console doctrine:generate:entities Acme/UserBundle/Entity/User``.
204+
For more details, see Doctrine's :ref:`book-doctrine-generating-getters-and-setters`.
205+
206+
Below is an export of my ``User`` table from MySQL with user `admin`
207+
and password `admin`. For details on how to create user records and
208+
encode their password, see :ref:`book-security-encoding-user-password`.
202209

203210
.. code-block:: bash
204211
205-
$ mysql> select * from user;
206-
+----+----------+----------------------------------+------------------------------------------+--------------------+-----------+
207-
| id | username | salt | password | email | is_active |
208-
+----+----------+----------------------------------+------------------------------------------+--------------------+-----------+
209-
| 1 | hhamon | 7308e59b97f6957fb42d66f894793079 | 09610f61637408828a35d7debee5b38a8350eebe | hhamon@example.com | 1 |
210-
| 2 | jsmith | ce617a6cca9126bf4036ca0c02e82dee | 8390105917f3a3d533815250ed7c64b4594d7ebf | jsmith@example.com | 1 |
211-
| 3 | maxime | cd01749bb995dc658fa56ed45458d807 | 9764731e5f7fb944de5fd8efad4949b995b72a3c | maxime@example.com | 0 |
212-
| 4 | donald | 6683c2bfd90c0426088402930cadd0f8 | 5c3bcec385f59edcc04490d1db95fdb8673bf612 | donald@example.com | 1 |
213-
+----+----------+----------------------------------+------------------------------------------+--------------------+-----------+
214-
4 rows in set (0.00 sec)
215-
216-
The database now contains four users with different usernames, emails and
217-
statuses. The next part will focus on how to authenticate one of these users
212+
$ mysql> select * from acme_users;
213+
+----+----------+------+------------------------------------------+--------------------+-----------+
214+
| id | username | salt | password | email | is_active |
215+
+----+----------+------+------------------------------------------+--------------------+-----------+
216+
| 1 | admin | | d033e22ae348aeb5660fc2140aec35850c4da997 | admin@example.com | 1 |
217+
+----+----------+------+------------------------------------------+--------------------+-----------+
218+
219+
.. tip::
220+
221+
To generate database table from your ``User`` entity, you can run
222+
``php app/console doctrine:schema:update --force``.
223+
For mor details, see Doctrine's :ref:`book-doctrine-creating-the-database-tables-schema`.
224+
225+
The next part will focus on how to authenticate one of these users
218226
thanks to the Doctrine entity user provider and a couple of lines of
219227
configuration.
220228
@@ -329,9 +337,8 @@ entity user provider to load User entity objects from the database by using
329337
the ``username`` unique field. In other words, this tells Symfony how to
330338
fetch the user from the database before checking the password validity.
331339
332-
This code and configuration works but it's not enough to secure the application
333-
for **active** users. As of now, you can still authenticate with ``maxime``. The
334-
next section explains how to forbid non active users.
340+
This code is not enough to secure the application for **active** users.
341+
The next section explains how to forbid non active users.
335342
336343
Forbid non Active Users
337344
-----------------------
@@ -361,10 +368,10 @@ For this example, the first three methods will return ``true`` whereas the
361368
// src/Acme/UserBundle/Entity/User.php
362369
namespace Acme\UserBundle\Entity;
363370
364-
// ...
371+
use Doctrine\ORM\Mapping as ORM;
365372
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
366373
367-
class User implements AdvancedUserInterface
374+
class User implements AdvancedUserInterface, \Serializable
368375
{
369376
// ...
370377
@@ -389,10 +396,8 @@ For this example, the first three methods will return ``true`` whereas the
389396
}
390397
}
391398
392-
If you try to authenticate as ``maxime``, the access is now forbidden as this
393-
user does not have an enabled account. The next session will focus on how
394-
to write a custom entity provider to authenticate a user with his username
395-
or his email address.
399+
The next session will focus on how to write a custom entity provider
400+
to authenticate a user with his username or his email address.
396401
397402
Authenticating Someone with a Custom Entity Provider
398403
----------------------------------------------------
@@ -434,8 +439,7 @@ The code below shows the implementation of the
434439
->where('u.username = :username OR u.email = :email')
435440
->setParameter('username', $username)
436441
->setParameter('email', $username)
437-
->getQuery()
438-
;
442+
->getQuery();
439443
440444
try {
441445
// The Query::getSingleResult() method throws an exception
@@ -543,10 +547,11 @@ about in this section.
543547
authenticated at all.
544548
545549
In this example, the ``AcmeUserBundle:User`` entity class defines a
546-
many-to-many relationship with a ``AcmeUserBundle:Group`` entity class. A user
547-
can be related to several groups and a group can be composed of one or
548-
more users. As a group is also a role, the previous ``getRoles()`` method now
549-
returns the list of related groups::
550+
many-to-many relationship with a ``AcmeUserBundle:Role`` entity class.
551+
A user can be related to several roles and a role can be composed of
552+
one or more users. The previous ``getRoles()`` method now returns
553+
the list of related roles.
554+
Notice that methods ``__construcotor()`` and ``getRoles()`` had changed::
550555
551556
// src/Acme/UserBundle/Entity/User.php
552557
namespace Acme\UserBundle\Entity;
@@ -556,63 +561,46 @@ returns the list of related groups::
556561
557562
class User implements AdvancedUserInterface, \Serializable
558563
{
564+
//...
565+
559566
/**
560-
* @ORM\ManyToMany(targetEntity="Group", inversedBy="users")
567+
* @ORM\ManyToMany(targetEntity="Role", inversedBy="users")
561568
*
562569
*/
563-
private $groups;
570+
private $roles;
564571
565572
public function __construct()
566573
{
567-
$this->groups = new ArrayCollection();
574+
$this->roles = new ArrayCollection();
568575
}
569576
570-
// ...
571-
572577
public function getRoles()
573578
{
574-
return $this->groups->toArray();
575-
}
576-
577-
/**
578-
* @see \Serializable::serialize()
579-
*/
580-
public function serialize()
581-
{
582-
return serialize(array(
583-
$this->id,
584-
));
579+
return $this->roles->toArray();
585580
}
581+
582+
// ...
586583
587-
/**
588-
* @see \Serializable::unserialize()
589-
*/
590-
public function unserialize($serialized)
591-
{
592-
list (
593-
$this->id,
594-
) = unserialize($serialized);
595-
}
596584
}
597585
598-
The ``AcmeUserBundle:Group`` entity class defines three table fields (``id``,
586+
The ``AcmeUserBundle:Role`` entity class defines three table fields (``id``,
599587
``name`` and ``role``). The unique ``role`` field contains the role name used by
600588
the Symfony security layer to secure parts of the application. The most
601-
important thing to notice is that the ``AcmeUserBundle:Group`` entity class
589+
important thing to notice is that the ``AcmeUserBundle:Role`` entity class
602590
extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`::
603591
604-
// src/Acme/Bundle/UserBundle/Entity/Group.php
592+
// src/Acme/Bundle/UserBundle/Entity/Role.php
605593
namespace Acme\UserBundle\Entity;
606594
607-
use Symfony\Component\Security\Core\Role\Role;
595+
use Symfony\Component\Security\Core\Role\RoleInterface;
608596
use Doctrine\Common\Collections\ArrayCollection;
609597
use Doctrine\ORM\Mapping as ORM;
610598
611599
/**
612-
* @ORM\Table(name="acme_groups")
600+
* @ORM\Table(name="acme_roles")
613601
* @ORM\Entity()
614602
*/
615-
class Group extends Role
603+
class Role implements RoleInterface
616604
{
617605
/**
618606
* @ORM\Column(name="id", type="integer")
@@ -632,7 +620,7 @@ extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`::
632620
private $role;
633621
634622
/**
635-
* @ORM\ManyToMany(targetEntity="User", mappedBy="groups")
623+
* @ORM\ManyToMany(targetEntity="User", mappedBy="roles")
636624
*/
637625
private $users;
638626
@@ -641,21 +629,27 @@ extends the :class:`Symfony\\Component\\Security\\Core\\Role\\Role`::
641629
$this->users = new ArrayCollection();
642630
}
643631
644-
// ... getters and setters for each property
645-
646632
/**
647633
* @see RoleInterface
648634
*/
649635
public function getRole()
650636
{
651637
return $this->role;
652638
}
639+
640+
// ... getters and setters for each property
653641
}
654642
655-
To improve performances and avoid lazy loading of groups when retrieving a user
656-
from the custom entity provider, the best solution is to join the groups
643+
.. tip::
644+
645+
To generate missing setters and getters for your ``Role`` entity, you
646+
can use ``php app/console doctrine:generate:entities Acme/UserBundle/Entity/User``.
647+
For more details, see Doctrine's :ref:`book-doctrine-generating-getters-and-setters`.
648+
649+
To improve performances and avoid lazy loading of roles when retrieving a user
650+
from the custom entity provider, the best solution is to join the roles
657651
relationship in the ``UserRepository::loadUserByUsername()`` method. This will
658-
fetch the user and his associated roles / groups with a single query::
652+
fetch the user and his associated roles with a single query::
659653
660654
// src/Acme/UserBundle/Entity/UserRepository.php
661655
namespace Acme\UserBundle\Entity;
@@ -668,8 +662,8 @@ fetch the user and his associated roles / groups with a single query::
668662
{
669663
$q = $this
670664
->createQueryBuilder('u')
671-
->select('u, g')
672-
->leftJoin('u.groups', 'g')
665+
->select('u, r')
666+
->leftJoin('u.roles', 'r')
673667
->where('u.username = :username OR u.email = :email')
674668
->setParameter('username', $username)
675669
->setParameter('email', $username)
@@ -681,6 +675,29 @@ fetch the user and his associated roles / groups with a single query::
681675
// ...
682676
}
683677
684-
The ``QueryBuilder::leftJoin()`` method joins and fetches related groups from
678+
The ``QueryBuilder::leftJoin()`` method joins and fetches related roles from
685679
the ``AcmeUserBundle:User`` model class when a user is retrieved with his email
686680
address or username.
681+
682+
To re-generate all database tables, you can run ``php app/console doctrine:schema:update --force``.
683+
This will also create additional table ``user_role`` what holds
684+
relations between users and roles.
685+
For mor details, see Doctrine's :ref:`book-doctrine-creating-the-database-tables-schema`.
686+
687+
Below is an export of my ``Roles`` and ``user_role`` tables from MySQL:
688+
689+
.. code-block:: bash
690+
691+
$ mysql> select * from acme_users;
692+
+----+-------+------------+
693+
| id | name | role |
694+
+----+-------+------------+
695+
| 1 | admin | ROLE_ADMIN |
696+
+----+-------+------------+
697+
698+
mysql> select * from user_role;
699+
+---------+---------+
700+
| user_id | role_id |
701+
+---------+---------+
702+
| 1 | 1 |
703+
+---------+---------+

0 commit comments

Comments
 (0)