Obtenga todos los usuarios con roles específicos utilizando EntityFieldQuery


35

Pensé que era una tarea fácil, sin embargo, no parece haber un método Drupal para esto. Llegué a saber que tengo que usar EntityFieldQuerypara esto, porque la API dijo que las condiciones user_load_multiple()están en desuso.

Entonces intenté esto:

  $query = new EntityFieldQuery;
  $query
    ->entityCondition('entity_type', 'user')
    ->propertyCondition('rid',array(1,2,3);

  $result = $query->execute();

Sin embargo, tengo esto:

PDOException: SQLSTATE [42S22]: Columna no encontrada: 1054 Columna desconocida 'users.rid' en 'cláusula where': SELECT users.uid AS entity_id,: entity_type AS entity_type, NULL AS revision_id,: bundle AS bundle FROM {users} usuarios WHERE (users.rid =: db_condition_placeholder_0); Matriz ([: db_condition_placeholder_0] => 3 [: entity_type] => user [: bundle] => user) en EntityFieldQuery-> execute ()

Así que lo primero que pensé fue que tendría que unirse con users_roles-Tabla y así sucesivamente, pero que voy a llevar a los duplicados.

¿Alguien tiene una idea de cómo hacerlo?


user_load_multiple () también es un no-go. Esperemos que tengamos una manera de hacer esto en D8.
Dalin

Puede usar mi sandbox EntityFieldQueryExtra como lo permita ->propertyCondition('rid', array(1, 2, 3));
mikeytown2

Respuestas:


32

Para ser honesto, no tengo idea de cómo lograr esto. Buenos ejemplos de cómo usar EntityFieldQuery son difíciles de encontrar. Como nadie ha respondido esta pregunta todavía y también estoy interesado en la solución, intentaré ayudarte. A continuación se describe mi mejor suposición.

Una cosa a tener en cuenta: los roles se almacenan en una tabla diferente a la de los usuarios. Se agregan usando UserController :: attachLoad .

Parece que hay tres condiciones diferentes que puede usar con un EntityFieldQuery:

  1. entityCondition (Condiciones específicas de la entidad: 'entity_type', 'bundle', 'revision_id' o 'entity_id')
  2. fieldCondition (Condiciones en los campos mantenidos por la API de campo)
  3. propertyCondition (Condiciones de las propiedades de la entidad)

La opción más lógica (si hay una) sería usar una propiedad Condición , pero como los roles se agregan al usuario en UserController :: attachLoad , no creo que EntityFieldQuery tenga acceso a él. Creo que EntityFieldQuery solo usa el esquema definido en hook_schema ().

Esto me lleva a creer que lo que estás tratando de lograr es imposible. Una solución alternativa sería obtener todos los uid con una consulta normal:

No tengo acceso a Drupal en este momento, por lo que el siguiente código podría estar desactivado. Estoy seguro de que alguien editará los errores.

// Use $query for readability
$query = 'SELECT DISTINCT(ur.uid) 
  FROM {users_roles} AS ur
  WHERE ur.rid IN (:rids)';
$result = db_query($query, array(':rids' => array(1,2,3)));

$uids = $result->fetchCol();

$users = user_load_multiple($uids);

Si es posible lograr lo que quiere con EntityFieldQuery, me iluminaré.


1
Sí, así es como fui. Sin embargo, tengo mucha curiosidad sobre cómo lograr esto con EFQ y dejaré la pregunta abierta. Es una pena que los EFQ aún no estén tan bien documentados, ya que a la larga podrían reemplazarme por completo.
Nils Riedemann

EFQ solo admite propiedades básicas de una entidad, que forman parte de la tabla de entidades. Los roles de un usuario no están expuestos de ninguna manera al sistema de la entidad.
Berdir

1
Además, sugerencia de optimización: puede reemplazar la inicialización de $ uids y el foreach con $uids = $result->fetchColumn().
Berdir

Siéntase libre de editar el código Berdir :)
Bart

19

Debería hacer el truco

  $query = new EntityFieldQuery;
  $query
    ->entityCondition('entity_type', 'user')
    ->addTag('role_filter');
  $results = $query->execute();

/**
* Implement hook_query_TAG_alter
* 
* @param QueryAlterableInterface $query
*/
function MY_MODULE_query_role_filter_alter(QueryAlterableInterface $query) {
  $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');  
  $and = db_and()
            ->condition('r.rid', MY_ROLE_INT, '=');
  $query
    ->condition($and);
}

Me pregunto por qué esta respuesta tiene tan pocos votos positivos. Es la mejor solución. Aunque debo tener en cuenta que $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');podría causar una columna desconocida 'uid'. En ese caso, necesitamos agregar $query->propertyCondition('uid', 0, '!='), por lo que la tabla de usuarios estará en la consulta.
Elaman

1
Definitivamente esta debería ser la respuesta aceptada.
Frank Robert Anderson

16

De hecho, hay una manera de hacer esto. En esencia, el EntityFieldQuery (EFQ) es solo una consulta de base de datos que puede modificarse con enlaces de consulta.

El ejemplo más simple posible:

function mymodule_get_users_by_rolename($rolename){
  $query = new EntityFieldQuery;
  $query->entityCondition('entity_type', 'user');
  $query->addTag('rolequery');
  $query->addMetaData('rolename', $rolename);

  return $query->execute();
}

function mymodule_query_rolequery_alter(QueryAlterableInterface $query) {
  $rolename = $query->getMetaData('rolename');

  $role_subquery = db_select("role", "role");
  $role_subquery->condition('role.name', $rolename, '=');
  $role_subquery->join('users_roles', "users_to_include", "role.rid = users_to_include.rid");
  $role_subquery->fields('users_to_include', array('uid' => 'uid'));
  $role_subquery->where('users_to_include.uid = users.uid');
  $query->exists($role_subquery);
}

Sin embargo, hay algunas pequeñas advertencias con esto que requerirían más codificación. Por ejemplo, en el cuidado de la única condición presente en el EFQ que es una fieldCondition, la tabla de base de usuarios no estará presente, por lo que cuando busque usuarios por rol y una sola fieldCondition, también deberá asegurarse de que la tabla de usuarios está unida y, si no, únala manualmente, lo cual es un proceso un poco tedioso.

Pero para su necesidad, esto hará el truco. Al darse cuenta de que puede modificar manualmente las consultas de EFQ, se abre un enorme mundo de oportunidades para crear consultas muy potentes y, al mismo tiempo, mantener la interfaz limpia y Drupal.

Avíseme si tiene alguna pregunta o problema.

Editar: En realidad, encontré una forma un poco más eficiente de hacer esto y cambié mi código en consecuencia.


Una forma de "piratear" el requisito de la tabla base es agregar siempre una condición de propiedad falsa como $ query-> propertyCondition ('uid', 0, '! ='); a la consulta. Eso obliga a vincular la tabla base, pero obstaculiza el rendimiento solo un poco, ya que la razón por la cual EFQ deja de lado la tabla base en el caso de un solo campo La condición es que es mucho más rápido consultar la tabla de datos de campo.
Tommi Forsström

2
No creo que este sea el ejemplo más simple posible, como se afirma. ¿No puedes deshacerte de la subconsulta en favor de dos uniones y una condición directamente $query? Y también podría sugerir cambiar mymodule_get_users_by_rolenamepara no devolver el resultado de la consulta en sí, sino para desreferenciar la clave 'user'.
Peter Taylor

6
$user_list = array();
$roles = array('role_1', 'role_2');
$query = db_select('users_roles', 'ur');
$query->join('users', 'u', 'u.uid = ur.uid');
$query->join('role', 'r', 'r.rid = ur.rid');
$query->fields('u',array('uid'));
$query->fields('u',array('mail'));
$query->fields('u',array('name'));

$query->condition('r.name', $roles, 'IN');
$result = $query->execute();

if($result){
    foreach($result as $row ) {
        $uid = $row->uid;
        $user_list[$uid]['mail'] = $row->mail;
        $user_list[$uid]['name'] = $row->name;
        $user_list[$uid]['uid'] = $uid;
    }
}

return $user_list;

5

Todavía puede usar EntityFieldQuery () una vez que obtenga los identificadores de usuario para su rol, si lo desea, por ejemplo, porque desea usar fieldCondition () u otro método.

Usando la matriz $ uids en el ejemplo anterior:

$role = user_role_load_by_name('my_role_name');
$result = db_select('users_roles', 'ur')
->fields('ur', array('uid'))
->condition('rid', $role->rid)
->execute();

foreach($result as $record) {
  $uids[] = $record->uid;
}

$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'user')
->entityCondition('entity_id', $uids, 'IN')
->execute();

Sería mejor si EntityFieldQuery tuviera un método de unión.


1
Si tiene más de unas pocas docenas de usuarios con este rol, este EFQ tendrá un rendimiento terrible.
Dalin

5

/**
 * Get users by specific role
 */
function mymodule_get_users_by_role() {
    $role = user_role_load_by_name('rolename');
    $uids = db_select('users_roles', 'ur')
        ->fields('ur', array('uid'))
        ->condition('ur.rid', $role->rid, '=')
        ->execute()
        ->fetchCol();
    $users = user_load_multiple($uids);
}

Creo que esta es probablemente la respuesta más eficiente. Como una ligera mejora, agregaría un parámetro $rolenameque se usaría en la llamada user_role_load_by_namey una verificación para asegurarme de user_role_load_by_nameque no devuelva falso.
stefgosselin

4

OK Esta es una vieja, pero podrías hacer algo como esto:

$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'user')

$roles_subquery = db_select('users_roles', 'ur');
$roles_subquery->fields('ur', array('uid'));
$roles_subquery->condition('rid', $my_role_id);

$query->propertyCondition('uid', $roles_subquery, 'IN');

Cambié de opinión, esta es la mejor respuesta. Excepto, te falta un punto y coma y $ my_role_id no está definido.
Frank Robert Anderson

2

Esto es un poco tarde para el juego, pero creo que es lo que pedía el OP. por ejemplo, no sql, todo drupal. Esperemos que otros lo encuentren útil. La búsqueda de esto me llevó aquí y esto es lo que se me ocurrió.

    /**
     * Users with role
     *
     * @param $role mixed The name or rid of the role we're wanting users to have
     * @param $active_user boolean Only return active accounts?
     *
     * @return array An array of user objects with the role
     */
    function users_with_role($role, $active_user = TRUE) {
      $uids = array();
      $users = array();
      if (is_int($role)) {
        $my_rid = $role;
      }
      else {
        $role_obj = user_role_load_by_name($role);
      }
      $result = db_select('users_roles', 'ur')
        ->fields('ur')
        ->condition('ur.rid', $role_obj->rid, '=')
        ->execute();
      foreach ($result as $record) {
        $uids[] = $record->uid;
      };
      $query = new EntityFieldQuery();
      $query->entityCondition('entity_type', 'user')
        ->propertyCondition('uid', $uids, 'IN');
      if ($active_user) {
        $query->propertyCondition('status', 1);
      }
      $entities = $query->execute();
      if (!empty($entities)) {
        $users = entity_load('user', array_keys($entities['user']));
      }
      return $users;
    }

Si tiene más de unas pocas docenas de usuarios con este rol, este EFQ tendrá un rendimiento terrible.
Dalin

@Dalin, dada la calificación inicial, (es decir, sin sql, todo drupal), ¿qué sugeriría como método para obtener un mejor rendimiento?
Oro

1
Hola oro! Parece que la condición db_select () espera que $ role haya sido una cadena, y si pasa un número entero, quedará atrapado en $ role_obj que no se establece cuando hace referencia a $ role_obj-> rid. EDITAR: puedo editar respuestas, woo.
Chris Burgess

0

Este es de hecho un tema antiguo y encontré muchos buenos enfoques.

Mejoraría un poco la solución provista por @alf, porque creo que una consulta con join es el mejor enfoque. Sin embargo, también me gusta que mis funciones tengan parámetros y no dependan de una constante. Así que aquí va:

function MY_MODULE_load_users_by_role($rid) {
  $query = new EntityFieldQuery;
  $query
    ->entityCondition('entity_type', 'user')
    ->addMetaData('account_role', user_role_load($rid))
    ->addTag('role_filter');
  $results = $query->execute();

  if (isset($results['user'])) {
    $uids = array_keys($results['user']);
    $users = entity_load('user', $uids);
  }
  else {
    $users = array();
  }

  return $users;
}

/**
 * Implement hook_query_TAG_alter
 *
 * @param QueryAlterableInterface $query
 */
function MY_MODULE_query_role_filter_alter(QueryAlterableInterface $query) {
  $rid = $query->alterMetaData['account_role']->rid;
  $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');
  $and = db_and()
    ->condition('r.rid', $rid, '=');
  $query
    ->condition($and);
}

-1

Puede encontrar buenos ejemplos de explicaciones sobre cómo usar EntityFieldQuery aquí:

pero tal como Berdir dijo por ahora "EFQ solo admite propiedades básicas de una entidad, que son parte de la tabla de entidades. Las funciones de un usuario no están expuestas de ninguna manera al sistema de entidades".


-2

No tengo dudas de que esto se puede simplificar. Sin embargo, esto se puede hacer para drupal 8:

//retrieve all "VIP" members
$query = \Drupal::entityQuery('user');
$nids = $query->execute();
foreach ($nids as $nid){
    $user = \Drupal\user\Entity\User::load($nid);
    if ($user->hasRole('VIP'))
        $emails_VIP_members[]=$user->getEmail();
}
 $send_to=implode(',',$emails_VIP_members);

Es mejor usar la consulta directamente para recopilar los correos de los usuarios, en lugar de cargarlos todos
Codium

¿Es esta solución en una de esas respuestas?
Matoeil
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.