web-dev-qa-db-fra.com

Obtenez tous les utilisateurs avec des rôles spécifiques en utilisant EntityFieldQuery

Je pensais que c'était une tâche facile, mais il ne semble pas y avoir de méthode Drupal pour cela. J'en suis venu à savoir que je dois utiliser EntityFieldQuery pour cela - car l'API a dit que les conditions pour user_load_multiple() sont obsolètes.

J'ai donc essayé ceci:

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

  $result = $query->execute();

Pourtant, j'ai compris ceci:

PDOException: SQLSTATE [42S22]: Colonne introuvable: 1054 Colonne inconnue 'users.rid' dans 'clause where': SELECT users.uid AS entity_id,: entity_type AS entity_type, NULL AS revision_id,: bundle AS bundle FROM {users} users OERE (users.rid =: db_condition_placeholder_0); Tableau ([: db_condition_placeholder_0] => 3 [: entity_type] => utilisateur [: bundle] => utilisateur) dans EntityFieldQuery-> execute ()

Donc ma première pensée a été que je devais rejoindre users_roles- Tableau et ainsi de suite, mais cela entraînera des doublons.

Quelqu'un at-il une idée de comment le faire?

35
Nils Riedemann

Pour être honnête, je n'ai aucune idée de la façon d'y parvenir. Il est difficile de trouver de bons exemples d'utilisation de EntityFieldQuery. Puisque personne n'a encore répondu à cette question et que je suis également intéressé par la solution, je vais essayer de vous aider. Décrit ci-dessous est ma meilleure estimation.

Une chose à garder à l'esprit: les rôles sont stockés dans une table différente de celle des utilisateurs. Ils sont ajoutés à l'aide de serController :: attachLoad .

Il semble y avoir trois conditions différentes que vous pouvez utiliser avec une EntityFieldQuery:

  1. entityCondition (Conditions spécifiques à l'entité: 'entity_type', 'bundle', 'revision_id' ou 'entity_id')
  2. fieldCondition (Conditions sur les champs gérés par l'API Field)
  3. propertyCondition (Conditions sur les propriétés de l'entité)

L'option la plus logique (s'il y en a une) serait d'utiliser un propertyCondition, mais comme les rôles sont ajoutés à l'utilisateur dans serController :: attachLoad je ne pense pas que EntityFieldQuery y ait accès. Je pense que EntityFieldQuery utilise simplement le schéma défini dans hook_schema ().

Cela m'amène à croire que ce que vous essayez d'atteindre est impossible. Une solution de contournement serait d'obtenir tous les uids avec une requête normale:

Je n'ai pas accès à Drupal pour le moment, donc le code ci-dessous est peut-être désactivé. Je suis sûr que quelqu'un modifiera les erreurs.

// 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);

S'il est possible de réaliser ce que vous voulez avec EntityFieldQuery, je serai éclairé.

32
Bart

Ça devrait faire l'affaire

  $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);
}
19
alf

Il existe en fait un moyen de le faire. En son cœur, l'EntityFieldQuery (EFQ) n'est qu'une requête de base de données qui peut être modifiée avec des crochets de modification de requête.

Exemple le plus simple possible:

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

Il y a cependant quelques petites mises en garde avec cela qui nécessiteraient un peu plus de codage. Par exemple, pour que la seule condition présente dans l'EFQ soit une condition de champ, la table de base des utilisateurs ne sera pas présente, donc lorsque vous récupérez des utilisateurs par rôle et une seule condition de champ, vous devez également vous assurer que la la table des utilisateurs est jointe et sinon, la joindre manuellement, ce qui est un peu un processus fastidieux.

Mais pour votre besoin, cela fera l'affaire. Le fait de savoir que vous pouvez modifier manuellement les requêtes EFQ ouvre un immense monde d'opportunités pour créer des requêtes très puissantes tout en gardant l'interface propre et Drupal.

Faites-moi savoir si vous avez des questions ou des problèmes.

Edit: J'ai en fait trouvé un moyen un peu plus performant de le faire et j'ai changé mon code en conséquence.

16
Tommi Forsström
$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;
6
Dhanesh Haridas

/**
 * 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);
}
5
ibit

Vous pouvez toujours utiliser EntityFieldQuery () une fois que vous avez obtenu les ID utilisateur de votre rôle, si vous le souhaitez, par exemple parce que vous souhaitez utiliser fieldCondition () ou une autre méthode.

En utilisant le tableau $ uids dans l'exemple ci-dessus:

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

Ce serait plus agréable si EntityFieldQuery avait une méthode de jointure.

5
JUPITER

OK C'est un ancien, mais vous pouvez faire quelque chose comme ça:

$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');
4
Fabio Epifani

C'est un peu tard pour le jeu, mais je pense que c'est ce que le PO demandait. par exemple. pas de sql, tout drupal. J'espère que d'autres le trouveront utile. La recherche de cela m'a conduit ici et c'est ce que j'ai trouvé.

    /**
     * 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;
    }
2
Gold

C'est en effet un sujet plus ancien et j'ai trouvé beaucoup de bonnes approches.

J'améliorerais un peu la solution fournie par @alf, car je pense qu'une requête avec join est la meilleure approche. Cependant, j'aime aussi que mes fonctions aient des paramètres et ne dépendent pas d'une constante. Alors voilà:

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);
}
0
benelori