Obter todos os usuários com funções específicas usando EntityFieldQuery

35

Eu pensei que era uma tarefa fácil, mas não parece haver um método Drupal para isso. Cheguei ao ponto de saber que tenho que usar EntityFieldQuerypara isso - porque a API disse que as condições user_load_multiple()estão obsoletas.

Então eu tentei isso:

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

  $result = $query->execute();

No entanto, eu entendi isso:

PDOException: SQLSTATE [42S22]: Coluna não encontrada: 1054 Coluna desconhecida 'users.rid' in 'where cláusula': SELECT users.uid AS entity_id,: entity_type AS entity_type, NULL AS revision_id,: bundle AS bundle FROM users {users} users WHERE (users.rid =: db_condition_placeholder_0); Matriz ([: db_condition_placeholder_0] => 3 [: entity_type] => user [: bundle] => user) em EntityFieldQuery-> execute ()

Então, meu primeiro pensamento foi que eu teria que se juntar com users_roles-Quadro e assim por diante, mas isso vai levar a duplicatas.

Alguém tem uma idéia de como fazer isso?

Nils Riedemann
fonte
user_load_multiple () também é proibido. Espero que tenhamos uma maneira de fazer isso no D8.
Dalin
Você pode usar minha caixa de proteção EntityFieldQueryExtra conforme ela permitir ->propertyCondition('rid', array(1, 2, 3));
precisa saber é o seguinte

Respostas:

32

Para ser sincero, não tenho idéia de como conseguir isso. É difícil encontrar bons exemplos de como usar o EntityFieldQuery. Como ninguém respondeu a essa pergunta ainda e também estou interessado na solução, tentarei ajudá-lo. Descrito abaixo é o meu melhor palpite.

Um aspecto a ter em mente: as funções são armazenadas em uma tabela diferente dos usuários. Eles são adicionados usando UserController :: attachLoad .

Parece haver três condições diferentes que você pode usar com um EntityFieldQuery:

  1. entityCondition (Condições específicas da entidade: 'entity_type', 'bundle', 'revision_id' ou 'entity_id')
  2. fieldCondition (Condições nos campos mantidos pela API do campo)
  3. propertyCondition (Condições nas propriedades da entidade)

A opção mais lógica (se houver) seria usar uma propertyCondition, mas conforme as funções são adicionadas ao usuário em UserController :: attachLoad Não acho que o EntityFieldQuery tenha acesso a ele. Eu acho que o EntityFieldQuery apenas usa o esquema definido em hook_schema ().

Isso me leva a acreditar que o que você está tentando alcançar é impossível. Uma solução alternativa seria obter todos os recursos com uma consulta normal:

Eu não tenho acesso ao Drupal agora, então o código abaixo pode estar desativado. Tenho certeza que alguém irá editar os erros.

// 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 for possível alcançar o que você deseja com EntityFieldQuery, eu serei esclarecido.

Bart
fonte
11
Sim, foi assim que eu fui. No entanto, estou muito curioso para conseguir isso com o EFQ e deixarei a pergunta em aberto. É uma pena que os EFQs ainda não estejam tão bem documentados, pois podem substituir totalmente o Views para mim a longo prazo.
Nils Riedemann 14/09
O EFQ suporta apenas propriedades básicas de uma entidade, que fazem parte da tabela de entidades. As funções de um usuário não são expostas de forma alguma ao sistema da entidade.
Berdir
11
Além disso, dica de otimização: você pode substituir a initalização $ uids e o foreach por $uids = $result->fetchColumn().
Berdir
Sinta-se livre para editar o código Berdir :)
Bart
19

Deve fazer o truque

  $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);
}
alf
fonte
Eu me pergunto por que essa resposta tem tão poucos votos positivos. É a melhor solução. Embora eu deva notar que isso $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');pode causar a coluna desconhecida 'uid'. Nesse caso, precisamos adicionar $query->propertyCondition('uid', 0, '!='), para que a tabela de usuários esteja na consulta.
Elämän
11
Definitivamente, essa deve ser a resposta aceita.
Frank Robert Anderson
16

De fato, existe uma maneira de fazer isso. Em essência, o EntityFieldQuery (EFQ) é apenas uma consulta ao banco de dados que pode ser alterada com ganchos de alteração de consulta.

Exemplo mais simples possível:

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

No entanto, existem algumas pequenas advertências com isso que exigiriam mais codificação. Por exemplo, ao cuidar da única condição presente no EFQ ser uma fieldCondition, a tabela de base do usuário não estará presente; portanto, quando você estiver buscando usuários por função e uma única fieldCondition, também será necessário garantir que o a tabela users é ingressada e, se não, ingressar manualmente, o que é um processo meio tedioso.

Mas, para sua necessidade, isso funcionará. Perceber que você pode alterar manualmente as consultas EFQ abre um imenso mundo de oportunidades para criar consultas muito poderosas, mantendo a interface limpa e Drupal.

Entre em contato se tiver alguma dúvida ou problema.

Edit: Na verdade, encontrei uma maneira um pouco mais eficiente de fazer isso e alterei meu código de acordo.

Tommi Forsström
fonte
Uma maneira de "hackear" o requisito da tabela básica é sempre adicionar uma condição de propriedade falsa, como $ query-> propertyCondition ('uid', 0, '! ='); para a consulta. Isso força a ligação da tabela de base, mas atrapalha um pouco o desempenho, pois o EFQ deixa de fora a tabela de referência no caso de um único fieldCondition é que é muito mais rápido consultar a tabela de dados do campo.
Tommi Forsström
2
Eu não acho que este seja o exemplo mais simples possível, como alegado. Você não pode abandonar a subconsulta em favor de duas junções e uma condição diretamente $query? E também posso sugerir alterar mymodule_get_users_by_rolenamepara não retornar o resultado da consulta em si, mas para desreferenciar a chave 'user'.
22613 Peter
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;
Dhanesh Haridas
fonte
5

Você ainda pode usar EntityFieldQuery () depois de obter os IDs de usuário para sua função, se desejar, por exemplo, porque deseja usar o fieldCondition () ou outro método.

Usando a matriz $ uids no exemplo acima:

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

Seria melhor se EntityFieldQuery tivesse um método de junção.

JÚPITER
fonte
11
Se você tiver mais de algumas dezenas de usuários com essa função, esse EFQ terá um desempenho terrível.
Dalin 22/05/2013
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);
}
eu mordo
fonte
Eu acho que essa é provavelmente a resposta mais eficiente. Como uma pequena melhoria, eu adicionaria um parâmetro $rolenameque seria usado na chamada user_role_load_by_namee uma verificação para garantir user_role_load_by_nameque não retorne falso.
stefgosselin
4

OK Este é antigo, mas você pode fazer algo assim:

$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');
Fabio Epifani
fonte
Mudei de idéia, esta é a melhor resposta. Exceto que falta um ponto-e-vírgula e $ my_role_id é indefinido.
31575 Robert Frank Anderson
2

É um pouco tarde para o jogo, mas acho que é o que o OP estava pedindo. por exemplo, sem sql, tudo drupal. Espero que outros achem útil. A busca por isso me levou aqui e foi isso que eu criei.

    /**
     * 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;
    }
Ouro
fonte
Se você tiver mais de algumas dezenas de usuários com essa função, esse EFQ terá um desempenho terrível.
Dalin 22/05/2013
@ Dalin, dada a qualificação inicial (ou seja, sem sql, tudo drupal), o que você sugeriria como um método que obteria melhor desempenho?
ouro
11
Hey Gold! Parece que a condição db_select () espera que $ role seja uma string e, se você passar um número inteiro, ele será pego em $ role_obj não sendo definido quando ele fizer referência a $ role_obj-> rid. EDIT: Eu posso editar respostas, woo.
Chris Burgess
0

Este é realmente um tópico mais antigo e eu encontrei muitas boas abordagens.

Eu melhoraria um pouco a solução fornecida pelo @alf, porque acho que uma consulta com join é a melhor abordagem. No entanto, também gosto que minhas funções tenham parâmetros e não dependam de uma constante. Então aqui vai:

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

Você pode encontrar bons exemplos de explicações sobre como usar o EntityFieldQuery aqui:

mas, como disse Berdir no momento, "o EFQ suporta apenas propriedades básicas de uma entidade, que fazem parte da tabela de entidades. As funções de um usuário não são expostas de forma alguma ao sistema da entidade".

k_zoltan
fonte
-2

Não tenho dúvida de que isso pode ser simplificado. Isso pode ser feito no 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);
Matoeil
fonte
Melhor usar a consulta diretamente para coletar e-mails do usuário, em vez de carregá-los todos
Codium
esta solução está em uma dessas respostas?
Matoeil 04/04