Ottieni tutti gli utenti con ruoli specifici utilizzando EntityFieldQuery


35

Ho pensato che fosse un compito facile, ma non sembra esserci un metodo Drupal per questo. Sono arrivato al punto di sapere che devo usare EntityFieldQueryper questo - perché l'API ha detto che le condizioni user_load_multiple()sono deprecate.

Quindi ho provato questo:

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

  $result = $query->execute();

Eppure ho ottenuto questo:

PDOException: SQLSTATE [42S22]: Colonna non trovata: 1054 Colonna sconosciuta 'users.rid' nella 'clausola where': SELECT users.uid AS entity_id,: entity_type AS entity_type, NULL AS revision_id,: bundle AS bundle FROM {users} users DOVE (users.rid =: db_condition_placeholder_0); Array ([: db_condition_placeholder_0] => 3 [: entity_type] => user [: bundle] => user) in EntityFieldQuery-> execute ()

Così il mio primo pensiero è stato, che avrei dovuto unirsi con users_roles-Table e così via, ma che sarà portare a duplicati.

Qualcuno ha un'idea di come farlo?


user_load_multiple () è anche un no-go. Speriamo di avere un modo per farlo in D8.
Dalin,

Puoi usare il mio sandbox EntityFieldQueryExtra come consente ->propertyCondition('rid', array(1, 2, 3));
mikeytown2,

Risposte:


32

Ad essere sincero, non ho idea di come raggiungere questo obiettivo. Buoni esempi di come usare EntityFieldQuery sono difficili da trovare. Poiché nessuno ha ancora risposto a questa domanda e sono anche interessato alla soluzione, cercherò di aiutarti. Di seguito è descritta la mia ipotesi migliore.

Una cosa da tenere a mente: i ruoli sono archiviati in una tabella diversa rispetto agli utenti. Vengono aggiunti utilizzando UserController :: attachLoad .

Sembrano esserci tre diverse condizioni che è possibile utilizzare con EntityFieldQuery:

  1. entityCondition (Condizioni specifiche dell'entità: 'entity_type', 'bundle', 'revision_id' o 'entity_id')
  2. fieldCondition (Condizioni sui campi gestiti dall'API Field)
  3. propertyCondition (Condizioni sulle proprietà dell'entità)

L'opzione più logica (se ce n'è una) sarebbe quella di utilizzare una proprietàCondition, ma poiché i ruoli vengono aggiunti all'utente in UserController :: attachLoad Non credo che EntityFieldQuery abbia accesso ad essa. Penso che EntityFieldQuery usi solo lo schema definito in hook_schema ().

Questo mi porta a credere che ciò che stai cercando di ottenere sia impossibile. Una soluzione alternativa sarebbe quella di ottenere tutti gli uid con una query normale:

Non ho accesso a Drupal in questo momento, quindi il codice qui sotto potrebbe essere spento. Sono sicuro che qualcuno modificherà gli errori.

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

Se è possibile ottenere ciò che vuoi con EntityFieldQuery, sarò illuminato.


1
Sì, è andata così. Tuttavia sono molto curioso di sapere come raggiungere questo obiettivo con EFQ e lascerò aperta la domanda. È un peccato che gli EFQ non siano ancora ben documentati in quanto potrebbero sostituire completamente Views per me nel lungo periodo.
Nils Riedemann,

EFQ supporta solo le proprietà di base di un'entità, che fanno parte della tabella delle entità. I ruoli di un utente non sono esposti in alcun modo al sistema di entità.
Berdir,

1
Inoltre, suggerimento per l'ottimizzazione: è possibile sostituire l'inizializzazione $ uids e il foreach con $uids = $result->fetchColumn().
Berdir,

Sentiti libero di modificare il codice Berdir :)
Bart,

19

Dovrebbe fare il trucco

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

Mi chiedo perché questa risposta abbia così pochi voti positivi. È la migliore soluzione Tuttavia, dovrei notare che $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');potrebbe causare la colonna sconosciuta "uid". In tal caso, dobbiamo aggiungere $query->propertyCondition('uid', 0, '!='), quindi la tabella degli utenti sarà nella query.
Elaman,

1
Sicuramente questa dovrebbe essere la risposta accettata.
Frank Robert Anderson,

16

C'è infatti un modo per farlo. Nel suo cuore, EntityFieldQuery (EFQ) è solo una query di database che può essere modificata con hook di modifica delle query.

L'esempio più semplice possibile:

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

Vi sono tuttavia alcune piccole avvertenze che richiedono un po 'più di codifica. Ad esempio, nel caso in cui l'unica condizione presente nell'EFQ sia una condizione di campo, la base di utenti non sarà presente, quindi quando si recuperano gli utenti per ruolo e una singola condizione di campo, è necessario assicurarsi anche che la tabella degli utenti viene unita e, in caso contrario, la unisce manualmente, il che è un po 'noioso.

Ma per le tue necessità, questo farà il trucco. Rendersi conto che è possibile modificare manualmente le query EFQ apre un enorme mondo di opportunità per la creazione di query molto potenti, pur mantenendo l'interfaccia pulita e Drupal.

Fammi sapere se hai domande o problemi.

Modifica: in realtà ho trovato un modo un po 'più performante per farlo e ho modificato il mio codice di conseguenza.


Un modo per "hackerare" il requisito di base è quello di aggiungere sempre una condizione di proprietà fasulla come $ query-> propertyCondition ('uid', 0, '! ='); alla query. Ciò impone che la base possa essere collegata, ma ostacola leggermente le prestazioni poiché il motivo per cui EFQ esclude la base nel caso di un singolo campo Condizione è che è molto più veloce interrogare semplicemente la tabella dei dati del campo.
Tommi Forsström,

2
Non penso che questo sia l'esempio più semplice possibile, come affermato. Non puoi abbandonare la subquery a favore di due join e una condizione direttamente attiva $query? E potrei anche suggerire di cambiare mymodule_get_users_by_rolenameper non restituire il risultato della query stessa, ma per dereferenziare la chiave '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

È ancora possibile utilizzare EntityFieldQuery () una volta ottenuti gli ID utente per il proprio ruolo, se si desidera, ad esempio perché si desidera utilizzare fieldCondition () o un altro metodo.

Utilizzando l'array $ uids nell'esempio sopra:

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

Sarebbe più bello se EntityFieldQuery avesse un metodo join.


1
Se hai più di una dozzina di utenti con questo ruolo, questo EFQ avrà prestazioni terribili.
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);
}

Penso che questa sia probabilmente la risposta più efficace. Come un leggero miglioramento aggiungerei un parametro $rolenameche verrebbe utilizzato nella chiamata user_role_load_by_namee un controllo per assicurarsi user_role_load_by_nameche non restituisca false.
Stefgosselin,

4

OK Questo è vecchio, ma potresti fare qualcosa del genere:

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

Ho cambiato idea, questa è la risposta migliore. Tranne che ti manca un punto e virgola e $ my_role_id non è definito.
Frank Robert Anderson,

2

È un po 'tardi per il gioco, ma penso che sia ciò che l'OP stava chiedendo. ad esempio no sql, tutto drupal. Spero che altri lo trovino utile. La ricerca di questo mi ha portato qui ed è quello che mi è venuto in mente.

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

Se hai più di una dozzina di utenti con questo ruolo, questo EFQ avrà prestazioni terribili.
Dalin,

@Dalin, data la qualifica iniziale, (cioè no sql, tutto drupal), cosa suggeriresti come metodo per ottenere prestazioni migliori?
Oro

1
Ehi oro! Sembra che la condizione db_select () si aspetti che $ role sia stata una stringa e se passi un numero intero verrà catturato dal fatto che $ role_obj non viene impostato quando fa riferimento a $ role_obj-> rid. EDIT: posso modificare le risposte, woo.
Chris Burgess,

0

Questo è davvero un argomento più vecchio e ho trovato molti buoni approcci.

Vorrei migliorare un po 'la soluzione fornita da @alf, perché penso che una query con join sia l'approccio migliore. Tuttavia, mi piace anche che le mie funzioni abbiano parametri e non dipendano da una costante. Quindi ecco qui:

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

Puoi trovare esempi di buone spiegazioni su come usare EntityFieldQuery qui:

ma proprio come Berdir ha affermato come per ora "EFQ supporta solo le proprietà di base di un'entità, che fanno parte della tabella delle entità. I ​​ruoli di un utente non sono esposti in alcun modo al sistema di entità."


-2

Non ho dubbi che questo possa essere semplificato. Questo può essere fatto però per 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);

Meglio usare la query direttamente per raccogliere le mail degli utenti, invece di caricarle tutte
Codium

è questa soluzione in una di quelle risposte?
Matoeil
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.