使用EntityFieldQuery获取具有特定角色的所有用户


35

我以为这是一件容易的事,但是似乎没有Drupal方法。据我所知,我必须使用EntityFieldQuery它-因为API中提到的条件user_load_multiple()已弃用。

所以我尝试了这个:

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

  $result = $query->execute();

但是我得到了:

PDOException:SQLSTATE [42S22]:找不到列:1054“ where子句”中的未知列“ users.rid”:SELECT users.uid AS实体ID,:entity_type AS实体类型,NULL ASversion_id,:bundle AS捆绑包来自{users}个用户在哪里(users.rid =:db_condition_placeholder_0); EntityFieldQuery-> execute()中的数组([:db_condition_placeholder_0] => 3 [:entity_type] =>用户[:bundle] =>用户)

所以我首先想到的是,我必须加入users_roles-Table等,但这导致重复。

有谁知道如何做?


user_load_multiple()也不行。希望我们能在D8中做到这一点。
大林

您可以在允许的情况下使用我的EntityFieldQueryExtra沙箱->propertyCondition('rid', array(1, 2, 3))
mikeytown2 2014年

Answers:


32

老实说,我不知道如何实现这一目标。很难找到有关如何使用EntityFieldQuery的良好示例。由于尚未有人回答这个问题,并且我对解决方案也很感兴趣,因此我将尽力为您提供帮助。以下是我的最佳猜测。

要记住的一件事:角色与用户存储在不同的表中。使用UserController :: attachLoad添加它们。

EntityFieldQuery可以使用三种不同的条件:

  1. entityCondition(特定实体的条件:“ entity_type”,“ bundle”,“ revision_id”或“ entity_id”)
  2. fieldCondition(由Field API维护的字段条件)
  3. propertyCondition(有关实体属性的条件)

最合乎逻辑的选项(如果有)是使用propertyCondition,但是随着在UserController :: attachLoad中向用户添加角色,我不认为EntityFieldQuery可以访问它。我认为EntityFieldQuery只使用hook_schema()中定义的架构。

这使我相信,您要实现的目标是不可能的。一种解决方法是使用常规查询获取所有uid:

我目前无法访问Drupal,因此下面的代码可能已关闭。我确定有人会编辑这些错误。

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

如果可以通过EntityFieldQuery实现您想要的功能,那么我将得到启发。


1
是的,这就是我走的路。但是我很好奇如何使用EFQ来实现这一目标,并且这个问题尚待解决。EFQ记录得不够好,这很可惜,因为从长远来看,它们可能会完全取代Views。
尼尔斯·里德曼

EFQ仅支持实体的基本属性,这些属性是实体表的一部分。用户的角色不会以任何方式暴露给实体系统。
贝尔迪尔2011年

1
此外,还有优化提示:您可以将替换$ uids初始化和foreach $uids = $result->fetchColumn()
贝迪尔(Berdir)2011年

随时编辑代码Berdir :)
Bart

19

它应该可以解决问题

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

我不知道为什么这个答案很少投票。这是最好的解决方案。虽然我应该注意,这$query->leftJoin('users_roles', 'r', 'users.uid = r.uid');可能会导致未知列“ uid”。在这种情况下,我们需要添加$query->propertyCondition('uid', 0, '!='),因此users表将在查询中。
Elaman 2015年

1
绝对应该是公认的答案。
Frank Robert Anderson

16

实际上,有一种方法可以做到这一点。从本质上讲,EntityFieldQuery(EFQ)只是一个数据库查询,可以使用查询alter hook对其进行更改。

最简单的例子:

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

但是,这里有一些小的警告,需要更多的编码。例如,考虑到EFQ中存在的唯一条件是fieldCondition,将不会显示基于用户的表,因此,当您通过角色和单个fieldCondition来获取用户时,还需要确保用户表已加入,如果没有,则手动将其加入,这是一个乏味的过程。

但是,根据您的需要,这可以解决问题。意识到您可以手动更改EFQ查询为创建非常强大的查询同时保持界面清洁和Drupal开辟了巨大的机会。

如果您有任何疑问或问题,请告诉我。

编辑:实际上,我发现这样做的性能更高一些,并相应地更改了我的代码。


一种“破解”基表要求的方法是始终添加一个伪造的属性条件,例如$ query-> propertyCondition('uid',0,'!=');。查询。这迫使将基表链接起来,但是由于EFQ在单个fieldCondition情况下会遗漏基表的原因,它只会稍微降低性能,因为查询字段数据表的方式更快。
TommiForsström2013年

2
我认为这并不是最简单的示例。您不能放弃子查询而直接使用两个联接和一个条件$query吗?而且我可能还建议更改mymodule_get_users_by_rolename为不返回查询本身的结果,而是取消对key的引用'user'
彼得·泰勒

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

一旦获得角色的用户ID(例如,因为要使用fieldCondition()或其他方法),您仍然可以使用EntityFieldQuery()。

在上面的示例中使用$ uids数组:

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

如果EntityFieldQuery有一个join方法,那就更好了。


1
如果您拥有数十个以上的角色,则此EFQ的性能将非常糟糕。
大林

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

我认为这很可能是最有效的答案。作为一个略微的改进,我将添加$rolename将在调用中使用的参数user_role_load_by_name和检查以确保user_role_load_by_name不会返回false。
stefgosselin

4

确定这是一个旧的,但您可以执行以下操作:

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

我改变了主意,这是最好的答案。除非您缺少分号,并且$ my_role_id是未定义的。
Frank Robert Anderson

2

这对游戏来说有点晚了,但我认为这是OP所要求的。例如没有SQL,所有的Drupal。希望其他人会发现它有用。对这个问题的搜寻将我引到了这里,这就是我想出的。

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

如果您拥有数十个以上的角色,则此EFQ的性能将非常糟糕。
大林

@Dalin,如果具有初始资格(例如,没有sql,则全部为drupal),那么您会建议哪种方法获得更好的性能?
2014年

1
嘿,金!看起来db_select()条件期望$ role已经是一个字符串,并且如果您传递一个整数,当它引用$ role_obj-> rid时,它将被捕获在未设置的$ role_obj中。编辑:我可以编辑答案,宇。
克里斯·伯吉斯

0

这确实是一个较旧的主题,我发现了很多好的方法。

我会改进@alf提供的解决方案,因为我认为使用join查询是最好的方法。但是,我也喜欢我的函数具有参数并且不依赖常量。因此,它去了:

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

您可以在此处找到有关如何使用EntityFieldQuery的良好解释示例:

但是正如Berdir所说的那样:“ EFQ仅支持实体的基本属性,这些属性是实体表的一部分。用户的角色不会以任何方式暴露给实体系统。”


-2

我毫不怀疑这可以简化。这可以通过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);

最好直接使用查询来收集用户邮件,而不是全部加载
Codium

这些答案之一就是解决方案吗?
Matoeil
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.