Rufen Sie mit EntityFieldQuery alle Benutzer mit bestimmten Rollen ab


35

Ich fand das eine leichte Aufgabe, aber es scheint keine Drupal-Methode dafür zu geben. Ich bin soweit gekommen, dass ich weiß, dass ich das verwenden muss EntityFieldQuery- weil die API besagte Bedingungen für user_load_multiple()veraltet sind.

Also habe ich Folgendes versucht:

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

  $result = $query->execute();

Trotzdem habe ich folgendes verstanden:

PDOException: SQLSTATE [42S22]: Spalte nicht gefunden: 1054 Unbekannte Spalte 'users.rid' in 'where-Klausel': SELECT users.uid AS entity_id,: entity_type AS entity_type, NULL AS revision_id,: Bündel AS Bündel FROM {users} users WHERE (users.rid =: db_condition_placeholder_0); Array ([: db_condition_placeholder_0] => 3 [: entity_type] => Benutzer [: Bundle] => Benutzer) in EntityFieldQuery-> execute ()

Mein erster Gedanke war also, dass ich mich mit users_roles-Table usw. verbinden müsste, aber das wird zu Duplikaten führen.

Hat jemand eine Idee, wie es geht?


user_load_multiple () ist auch ein No-Go. Hoffentlich haben wir einen Weg, dies in D8 zu tun.
Dalin

Sie können meine EntityFieldQueryExtra- Sandbox nach Bedarf verwenden ->propertyCondition('rid', array(1, 2, 3)).
mikeytown2

Antworten:


32

Um ehrlich zu sein, ich habe keine Ahnung, wie dies erreicht werden kann. Gute Beispiele für die Verwendung der EntityFieldQuery sind schwer zu finden. Da noch niemand diese Frage beantwortet hat und ich auch an der Lösung interessiert bin, werde ich versuchen, Ihnen zu helfen. Unten beschrieben ist meine beste Vermutung.

Beachten Sie: Rollen werden in einer anderen Tabelle als Benutzer gespeichert. Sie werden mit UserController :: attachLoad hinzugefügt .

Es scheint drei verschiedene Bedingungen zu geben, die Sie mit einer EntityFieldQuery verwenden können:

  1. entityCondition ( entitätsspezifische Bedingungen: 'entity_type', 'bundle', 'revision_id' oder 'entity_id')
  2. fieldCondition (Bedingungen für Felder, die von der Field API verwaltet werden)
  3. propertyCondition (Bedingungen für Entitätseigenschaften)

Die logischste Option (wenn es eine gibt) wäre die Verwendung einer propertyCondition, aber wenn dem Benutzer in UserController :: attachLoad Rollen hinzugefügt werden, hat die EntityFieldQuery meines Erachtens keinen Zugriff darauf. Ich denke, die EntityFieldQuery verwendet nur das in hook_schema () definierte Schema.

Dies lässt mich glauben, dass das, was Sie erreichen wollen, unmöglich ist. Eine Problemumgehung wäre, alle Benutzeroberflächen mit einer normalen Abfrage abzurufen:

Ich habe momentan keinen Zugriff auf Drupal, daher ist der folgende Code möglicherweise deaktiviert. Ich bin mir sicher, dass jemand die Fehler korrigieren wird.

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

Wenn es möglich ist, mit EntityFieldQuery das zu erreichen, was Sie wollen, werde ich aufgeklärt.


1
Ja, das ist der Weg, den ich gegangen bin. Ich bin jedoch sehr gespannt, wie dies mit EFQ erreicht werden kann, und werde die Frage offen lassen. Es ist schade, dass EFQs noch nicht so gut dokumentiert sind, da sie auf lange Sicht Views für mich vollständig ersetzen könnten.
Nils Riedemann,

EFQ unterstützt nur grundlegende Eigenschaften einer Entität, die Teil der Entitätstabelle sind. Die Rollen eines Benutzers sind in keiner Weise dem Entitätssystem ausgesetzt.
Berdir

1
Optimierungshinweis: Sie können die Initialisierung von $ uids und foreach durch ersetzen $uids = $result->fetchColumn().
Berdir

Fühlen Sie sich frei, den Code zu bearbeiten Berdir :)
Bart

19

Es sollte den Trick machen

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

Ich frage mich, warum diese Antwort so wenig positive Stimmen hat. Es ist die beste Lösung. $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');Allerdings sollte ich beachten, dass die unbekannte Spalte "UID" verursachen könnte. In diesem Fall müssen wir hinzufügen $query->propertyCondition('uid', 0, '!='), damit sich die Benutzertabelle in der Abfrage befindet.
Elaman

1
Dies sollte auf jeden Fall die akzeptierte Antwort sein.
Frank Robert Anderson

16

Es gibt tatsächlich einen Weg, dies zu tun. In seinem Herzen ist die EntityFieldQuery (EFQ) nur eine Datenbankabfrage, die mit Abfrage-Änderungs-Hooks geändert werden kann.

Einfachstes mögliches Beispiel:

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

Es gibt jedoch ein paar kleine Einschränkungen, die mehr Codierung erfordern würden. Wenn beispielsweise die einzige Bedingung, die in EFQ vorhanden ist, eine Feldbedingung ist, ist die Benutzertabelle nicht vorhanden. Wenn Sie also Benutzer nach Rolle und einer einzelnen Feldbedingung abrufen, müssen Sie auch sicherstellen, dass die Die Benutzertabelle wird verbunden, und wenn nicht, verbinden Sie sie manuell, was ein mühsamer Prozess ist.

Aber für Ihren Bedarf wird dies den Trick tun. Die Erkenntnis, dass Sie EFQ-Abfragen manuell ändern können, eröffnet eine Fülle von Möglichkeiten, sehr leistungsfähige Abfragen zu erstellen, während die Benutzeroberfläche sauber und Drupal bleibt.

Lassen Sie mich wissen, wenn Sie Fragen oder Probleme haben.

Bearbeiten: Ich habe tatsächlich eine etwas performantere Methode gefunden und meinen Code entsprechend geändert.


Eine Möglichkeit, die Basistabellenanforderung zu "hacken", besteht darin, immer eine falsche Eigenschaftsbedingung wie $ query-> propertyCondition ('uid', 0, '! =') Hinzuzufügen. zur Abfrage. Dadurch wird die Verknüpfung der Basistabelle erzwungen, die Leistung wird jedoch nur geringfügig beeinträchtigt, da EFQ die Basistabelle im Fall einer einzelnen Feldbedingung weglässt. Dies liegt daran, dass es viel schneller ist, nur die Felddatentabelle abzufragen.
Tommi Forsström

2
Ich denke nicht, dass dies, wie behauptet, das einfachste Beispiel ist. Können Sie die Unterabfrage nicht zugunsten von zwei Joins und einer Bedingung direkt aufgeben $query? Und ich könnte auch vorschlagen, zu ändern, mymodule_get_users_by_rolenameum nicht das Ergebnis der Abfrage selbst zurückzugeben, sondern den Schlüssel zu dereferenzieren '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

Sie können EntityFieldQuery () weiterhin verwenden, sobald Sie die Benutzer-IDs für Ihre Rolle erhalten, wenn Sie dies möchten, zum Beispiel, weil Sie fieldCondition () oder eine andere Methode verwenden möchten.

Verwenden des Arrays $ uids im obigen Beispiel:

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

Es wäre schöner, wenn EntityFieldQuery eine Join-Methode hätte.


1
Wenn Sie mehr als ein paar Dutzend Benutzer mit dieser Rolle haben, hat dieser EFQ eine schreckliche Leistung.
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);
}

Ich denke, das ist höchstwahrscheinlich die effizienteste Antwort. Als leichte Verbesserung würde ich einen Parameter hinzufügen $rolename, der im Aufruf von verwendet wird, user_role_load_by_nameund eine Überprüfung, um sicherzustellen, user_role_load_by_namedass nicht false zurückgegeben wird.
Stefgosselin

4

OK Dies ist eine alte, aber Sie könnten so etwas tun:

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

Ich habe es mir anders überlegt, das ist die beste Antwort. Es fehlt jedoch ein Semikolon und $ my_role_id ist undefiniert.
Frank Robert Anderson

2

Das ist etwas spät für das Spiel, aber ich denke, es ist das, wonach die OP gefragt hat. zB kein SQL, alles Drupal. Hoffentlich finden es andere nützlich. Die Suche danach hat mich hierher geführt und das ist mir eingefallen.

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

Wenn Sie mehr als ein paar Dutzend Benutzer mit dieser Rolle haben, hat dieser EFQ eine schreckliche Leistung.
Dalin

@Dalin, in Anbetracht der anfänglichen Qualifikation (dh kein SQL, alles Drupal), was würden Sie als Methode vorschlagen, die eine bessere Leistung erzielen würde?
Gold

1
Hey Gold! Es sieht so aus, als ob die Bedingung db_select () voraussetzt, dass $ role ein String ist, und wenn Sie eine Ganzzahl übergeben, wird $ role_obj nicht gesetzt, wenn auf $ role_obj-> rid verwiesen wird. BEARBEITEN: Ich kann Antworten bearbeiten, umwerben.
Chris Burgess

0

Dies ist in der Tat ein älteres Thema und ich habe viele gute Ansätze gefunden.

Ich würde die von @alf bereitgestellte Lösung ein wenig verbessern, da ich denke, dass eine Abfrage mit Join der beste Ansatz ist. Ich möchte aber auch, dass meine Funktionen Parameter haben und nicht von einer Konstanten abhängig sind. Also hier geht es:

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

Hier finden Sie gute Erklärungsbeispiele zur Verwendung der EntityFieldQuery:

Aber genau wie Berdir sagte "EFQ unterstützt nur grundlegende Eigenschaften einer Entität, die Teil der Entitätstabelle sind. Die Rollen eines Benutzers sind in keiner Weise für das Entitätssystem verfügbar."


-2

Ich habe keinen Zweifel, dass dies vereinfacht werden kann. Dies kann jedoch für Drupal 8 durchgeführt werden:

//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 ist besser, die Abfrage direkt zum Sammeln von Benutzer-E-Mails zu verwenden, anstatt sie alle zu
laden

Ist diese Lösung in einer dieser Antworten?
Matoeil
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.