web-dev-qa-db-fra.com

Bundle FOS - Comment sélectionner des utilisateurs avec un rôle spécifique?

J'utilise le bundle FOS et je veux récupérer tous les utilisateurs avec un ROLE donné de la base de données.

Quelle est la meilleure façon de procéder?

51
Visavì

Ajoutez simplement ceci dans votre dépôt utilisateur ou remplacez $this->_entityName par YourUserBundle:User:

/**
 * @param string $role
 *
 * @return array
 */
public function findByRole($role)
{
    $qb = $this->_em->createQueryBuilder();
    $qb->select('u')
        ->from($this->_entityName, 'u')
        ->where('u.roles LIKE :roles')
        ->setParameter('roles', '%"'.$role.'"%');

    return $qb->getQuery()->getResult();
}

Si vous utilisez des groupes FOSUser, vous devez utiliser:

/**
 * @param string $role
 *
 * @return array
 */
public function findByRole($role)
{
    $qb = $this->_em->createQueryBuilder();
    $qb->select('u')
        ->from($this->_entityName, 'u')
        ->leftJoin('u.groups', 'g')
        ->where($qb->expr()->orX(
            $qb->expr()->like('u.roles', ':roles'),
            $qb->expr()->like('g.roles', ':roles')
        ))
        ->setParameter('roles', '%"'.$role.'"%');

    return $qb->getQuery()->getResult();
}
89
Léo Benoist

Eh bien, s'il n'y a pas de meilleure solution, je pense que je vais passer à une requête DQL:

$query = $this->getDoctrine()->getEntityManager()
            ->createQuery(
                'SELECT u FROM MyBundle:User u WHERE u.roles LIKE :role'
            )->setParameter('role', '%"ROLE_MY_ADMIN"%');

$users = $query->getResult();
24
Visavì

Comme l'indique @Tirithen, le problème est que vous n'obtiendrez pas les utilisateurs dotés d'un rôle implicite en raison de la hiérarchie des rôles. Mais il existe un moyen de contourner ce problème!

Le composant de sécurité Symfony fournit un service qui nous donne tous les rôles enfants pour un rôle parent spécifique. Nous pouvons créer un service qui fait presque la même chose, mais il nous donne tous les rôles parent pour un rôle enfant donné.

Créer un nouveau service:

namespace Foo\BarBundle\Role;

use Symfony\Component\Security\Core\Role\RoleHierarchy;
use Symfony\Component\Security\Core\Role\Role;

/**
 * ReversedRoleHierarchy defines a reversed role hierarchy.
 */
class ReversedRoleHierarchy extends RoleHierarchy
{
    /**
     * Constructor.
     *
     * @param array $hierarchy An array defining the hierarchy
     */
    public function __construct(array $hierarchy)
    {
        // Reverse the role hierarchy.
        $reversed = [];
        foreach ($hierarchy as $main => $roles) {
            foreach ($roles as $role) {
                $reversed[$role][] = $main;
            }
        }

        // Use the original algorithm to build the role map.
        parent::__construct($reversed);
    }

    /**
     * Helper function to get an array of strings
     *
     * @param array $roleNames An array of string role names
     *
     * @return array An array of string role names
     */
    public function getParentRoles(array $roleNames)
    {
        $roles = [];
        foreach ($roleNames as $roleName) {
            $roles[] = new Role($roleName);
        }

        $results = [];
        foreach ($this->getReachableRoles($roles) as $parent) {
            $results[] = $parent->getRole();
        }

        return $results;
    }
}

Définissez votre service par exemple dans yaml et injectez-y la hiérarchie des rôles:

# Provide a service that gives you all parent roles for a given role.
foo.bar.reversed_role_hierarchy:
    class: Foo\BarBundle\Role\ReversedRoleHierarchy
    arguments: ["%security.role_hierarchy.roles%"]

Vous êtes maintenant prêt à utiliser la classe dans votre propre service. En appelant $injectedService->getParentRoles(['ROLE_YOUR_ROLE']);, vous obtiendrez un tableau contenant tous les rôles parent conduisant à la permission "ROLE_YOUR_ROLE". Requête pour les utilisateurs qui ont un ou plusieurs de ces rôles ... profit!

Par exemple, lorsque vous utilisez MongoDB, vous pouvez ajouter une méthode à votre référentiel de documents utilisateur:

/**
 * Find all users with a specific role.
 */
public function fetchByRoles($roles = [])
{
    return $this->createQueryBuilder('u')
        ->field('roles')->in($roles)
        ->sort('email', 'asc');
}

Je ne suis pas dans Doctrine ORM mais je suis sûr que ce ne sera pas si différent.

10
Xatoo

Si vous avez cette exigence et que votre liste d'utilisateurs sera longue, vous aurez des problèmes de performances. Je pense que vous ne devriez pas stocker les rôles dans un champ sous forme de tableau sérialisé. Vous devez créer des rôles d'entité et une relation plusieurs à plusieurs avec la table users.

9
smoreno

Vous pouvez simplement utiliser ceci sur votre DQL:

SELECT u FROM YourFavouriteBundle:User u WHERE u.roles [NOT] LIKE '%ROLE_YOUR_ROLE%'

Bien sûr, avec QueryBuilder, il est plus élégant:

// $role = 'ROLE_YOUR_ROLE';
$qb->where('u.roles [NOT] LIKE :role')
   ->setParameter('role', "%$role%");
0
David Vartanian

Enfin je l'ai résolu, voici une solution exacte:

public function searchUsers($formData)
{
    $em = $this->getEntityManager();
    $usersRepository = $em->getRepository('ModelBundle:User');
    $qb = $usersRepository->createQueryBuilder('r');

    foreach ($formData as $field => $value) {
        if($field == "roles"){
            $qb->andWhere(":value_$field MEMBER OF r.roles")->setParameter("value_$field", $value);
        }else{
            $qb->andWhere("r.$field = :value_$field")->setParameter("value_$field", $value);
        }
    }
    return $qb->getQuery()->getResult();
}

À votre santé!

0
Muzafar Ali

Si vous devez filtrer les utilisateurs par rôle à l'aide d'un filtre DQL dans un fichier YAML (dans EasyAdminBundle par exemple)

entities:
    Admin:
        class: App\Entity\User
        list:
            dql_filter: "entity.roles LIKE '%%ROLE_ADMIN%%'"
0
Kaizoku Gambare