Пакет FOS - Как выбрать пользователей с определенной ролью?

Я использую пакет FOS и хочу получить всех пользователей с заданной ролью из базы данных.

Каков наилучший способ сделать это?

9 ответов

Решение

Просто добавьте это в свой UserRepository или замените $this->_entityName от 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();
}

Если вы используете FOSUser Groups, вы должны использовать:

/**
 * @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();
}

Ну, если нет лучшего решения, я думаю, что я пойду на запрос 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();

Если у вас есть это требование и ваш список пользователей будет обширным, у вас будут проблемы с производительностью. Я думаю, что вы не должны хранить роли в поле как сериализованный массив. Вы должны создать роли сущностей и отношения "многие ко многим" с таблицей пользователей.

Как утверждает @Tirithen, проблема заключается в том, что вы не получите пользователей с неявной ролью из-за иерархии ролей. Но есть способ обойти это!

Компонент безопасности Symfony предоставляет сервис, который предоставляет нам все дочерние роли для определенных родительских ролей. Мы можем создать сервис, который делает почти то же самое, только он дает нам все родительские роли для данной дочерней роли.

Создать новый сервис:

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;
    }
}

Определите ваш сервис, например, в yaml и добавьте в него иерархию ролей:

# 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%"]

Теперь вы готовы использовать класс в своем собственном сервисе. По телефону $injectedService->getParentRoles(['ROLE_YOUR_ROLE']); вы получите массив, содержащий все родительские роли, которые приведут к разрешению "ROLE_YOUR_ROLE". Запрос для пользователей, которые имеют одну или несколько из этих ролей... прибыль!

Например, когда вы используете MongoDB, вы можете добавить метод в ваш репозиторий пользовательских документов:

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

Я не в Доктрине ORM, но я уверен, что это не будет так по-другому.

Наконец-то я решил, вот точное решение:

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();
}

Ура!

Здесь я даю альтернативное решение:
я нахожу пользователей ролей для данного массива.
В контроллере я вызываю такую ​​​​функцию.

      $users = $userRepository->findUsersOfRoles(['ROLE_ADMIN', 'ROLE_SUPER_USER']);

Затем в моем репозитории я делаю цикл для создания условия и установки параметров:

      public function findUsersOfRoles($roles)
{
    $condition = 'u.roles LIKE :roles0';
    foreach ($roles as $key => $role){
        if ($key !== 0){
            $condition .= " OR u.roles LIKE :roles".$key;
        }
    }

    $query = $this->createQueryBuilder('u')
        ->where($condition);
    foreach ($roles as $key => $role){
        $query ->setParameter('roles'.$key, '%"'.$role.'"%');
    }

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

Вы можете использовать только это на своем DQL:

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

Конечно, с QueryBuilder это более элегантно:

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

Если вам нужно отфильтровать пользователей по ролям, используя фильтр DQL в файле YAML (например, в EasyAdminBundle)

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

ЭТО РЕШЕНИЕ ПЛОХО, но оно работает

Шаг 1: цикл через каждого пользователя
Шаг 2. Проверьте, есть ли у пользователя роль roleadmin
Шаг 3: создайте новый массив со всеми пользователями в роли администратора:

$allowedUsers = array();

        $em = $this->getDoctrine()->getEntityManager();

        $entities = $em->getRepository('YourBundle:User')->findAll();


        foreach($entities as $user){

            foreach($user->getRoles() as $role){
                if($role == "ROLE_ADMIN"){

                       $allowedUsers = $user;
                }

            }
        }

        return $allowedUsers;

Лучшее решение - сделать DQL-запрос, как писал Оливьеру.

Другие вопросы по тегам