Skip to content

Removing a section about Roles that I think has no real use-case #5184

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 17, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 0 additions & 207 deletions cookbook/security/entity_provider.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <book-security-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 <book-doctrine-generating-getters-and-setters>`:

.. 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
Expand Down