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?


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.


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

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.


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;

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.


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

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

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

-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".


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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.