diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index afdb719fd4a..a46fab48d02 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -525,213 +525,6 @@ This tells Symfony to *not* query automatically for the User. Instead, when someone logs in, the ``loadUserByUsername()`` method on ``UserRepository`` will be called. -Managing Roles in the Database ------------------------------- - -The end of this tutorial focuses on how to store and retrieve a list of roles -from the database. As mentioned previously, when your user is loaded, its -``getRoles()`` method returns the array of security roles that should be -assigned to the user. You can load this data from anywhere - a hardcoded -list used for all users (e.g. ``array('ROLE_USER')``), a Doctrine array -property called ``roles``, or via a Doctrine relationship, as you'll learn -about in this section. - -.. caution:: - - In a typical setup, you should always return at least 1 role from the ``getRoles()`` - method. By convention, a role called ``ROLE_USER`` is usually returned. - If you fail to return any roles, it may appear as if your user isn't - authenticated at all. - -.. caution:: - - In order to work with the security configuration examples on this page - all roles must be prefixed with ``ROLE_`` (see - the :ref:`section about roles ` in the book). For - example, your roles will be ``ROLE_ADMIN`` or ``ROLE_USER`` instead of - ``ADMIN`` or ``USER``. - -In this example, the ``AppBundle:User`` entity class defines a -many-to-many relationship with a ``AppBundle:Role`` entity class. -A user can be related to several roles and a role can be composed of -one or more users. The previous ``getRoles()`` method now returns -the list of related roles. Notice that ``__construct()`` and ``getRoles()`` -methods have changed:: - - // src/AppBundle/Entity/User.php - namespace AppBundle\Entity; - - use Doctrine\Common\Collections\ArrayCollection; - // ... - - class User implements AdvancedUserInterface, \Serializable - { - // ... - - /** - * @ORM\ManyToMany(targetEntity="Role", inversedBy="users") - * - */ - private $roles; - - public function __construct() - { - $this->roles = new ArrayCollection(); - } - - public function getRoles() - { - return $this->roles->toArray(); - } - - // ... - - } - -The ``AppBundle:Role`` entity class defines three fields (``id``, -``name`` and ``role``). The unique ``role`` field contains the role name -(e.g. ``ROLE_ADMIN``) used by the Symfony security layer to secure parts -of the application:: - - // src/AppBundle/Entity/Role.php - namespace AppBundle\Entity; - - use Symfony\Component\Security\Core\Role\RoleInterface; - use Doctrine\Common\Collections\ArrayCollection; - use Doctrine\ORM\Mapping as ORM; - - /** - * @ORM\Table(name="app_role") - * @ORM\Entity() - */ - class Role implements RoleInterface - { - /** - * @ORM\Column(name="id", type="integer") - * @ORM\Id() - * @ORM\GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @ORM\Column(name="name", type="string", length=30) - */ - private $name; - - /** - * @ORM\Column(name="role", type="string", length=20, unique=true) - */ - private $role; - - /** - * @ORM\ManyToMany(targetEntity="User", mappedBy="roles") - */ - private $users; - - public function __construct() - { - $this->users = new ArrayCollection(); - } - - /** - * @see RoleInterface - */ - public function getRole() - { - return $this->role; - } - - // ... getters and setters for each property - } - -For brevity, the getter and setter methods are hidden, but you can -:ref:`generate them `: - -.. code-block:: bash - - $ php app/console doctrine:generate:entities AppBundle/Entity/User - -Don't forget also to update your database schema: - -.. code-block:: bash - - $ php app/console doctrine:schema:update --force - -This will create the ``app_role`` table and a ``user_role`` that stores -the many-to-many relationship between ``app_user`` and ``app_role``. If -you had one user linked to one role, your database might look something like -this: - -.. code-block:: bash - - $ mysql> SELECT * FROM app_role; - +----+-------+------------+ - | id | name | role | - +----+-------+------------+ - | 1 | admin | ROLE_ADMIN | - +----+-------+------------+ - - $ mysql> SELECT * FROM user_role; - +---------+---------+ - | user_id | role_id | - +---------+---------+ - | 1 | 1 | - +---------+---------+ - -And that's it! When the user logs in, Symfony security system will call the -``User::getRoles`` method. This will return an array of ``Role`` objects -that Symfony will use to determine if the user should have access to certain -parts of the system. - -.. sidebar:: What's the purpose of the RoleInterface? - - Notice that the ``Role`` class implements - :class:`Symfony\\Component\\Security\\Core\\Role\\RoleInterface`. This is - because Symfony's security system requires that the ``User::getRoles`` method - returns an array of either role strings or objects that implement this interface. - If ``Role`` didn't implement this interface, then ``User::getRoles`` - would need to iterate over all the ``Role`` objects, call ``getRole`` - on each, and create an array of strings to return. Both approaches are - valid and equivalent. - -.. _cookbook-doctrine-entity-provider-role-db-schema: - -Improving Performance with a Join -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To improve performance and avoid lazy loading of roles when retrieving a user -from the custom entity provider, you can use a Doctrine join to the roles -relationship in the ``UserRepository::loadUserByUsername()`` method. This will -fetch the user and their associated roles with a single query:: - - // src/AppBundle/Entity/UserRepository.php - namespace AppBundle\Entity; - - // ... - - class UserRepository extends EntityRepository implements UserProviderInterface - { - public function loadUserByUsername($username) - { - $q = $this - ->createQueryBuilder('u') - ->select('u, r') - ->leftJoin('u.roles', 'r') - ->where('u.username = :username OR u.email = :email') - ->setParameter('username', $username) - ->setParameter('email', $username) - ->getQuery(); - - // ... - } - - // ... - } - -The ``QueryBuilder::leftJoin()`` method joins and fetches related roles from -the ``AppBundle:User`` model class when a user is retrieved by their email -address or username. - .. _`cookbook-security-serialize-equatable`: Understanding serialize and how a User is Saved in the Session