Drupal - Get all users with specific roles using EntityFieldQuery

To be honest, I have no idea on how to achieve this. Good examples of how to use the EntityFieldQuery are hard to find. Since noone has answered this question yet and I'm also interested in the solution I'll try to help you out. Described below is my best guess.

A thing to keep in mind: roles are stored in a different table than users. They are added using UserController::attachLoad.

There seem to be three different conditions that you can use with an EntityFieldQuery:

  1. entityCondition (Entity specific conditions:'entity_type', 'bundle', 'revision_id' or 'entity_id')
  2. fieldCondition (Conditions on fields maintained by the Field API)
  3. propertyCondition (Conditions on entity properties)

The most logical option (if there is one) would be to use a propertyCondition, but as roles are added to the user in UserController::attachLoad I don't think the EntityFieldQuery has access to it. I think the EntityFieldQuery just uses the schema defined in hook_schema().

This leads me to believe that what you're trying to achieve is impossible. A workaround would be to get all the uids with a normal query:

I don't have access to Drupal right now, so the code below might be off. I'm sure somebody will edit the mistakes.

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

If it is possible to achieve what you want with EntityFieldQuery, I'll be enlightened.


It should do the trick

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

There is in fact a way to do this. In its heart, the EntityFieldQuery (EFQ) is just a database query which can be altered with query alter hooks.

Simplest possible example:

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

There is however a few small caveats with this that would require some more coding. For example in the care of the only condition present in the EFQ being a fieldCondition, the users-basetable won't be present, so when you're fetching users by role and a single fieldCondition, you'd also need to make sure the users table is joined and if not, manually join it, which is a bit of a tedious process.

But for your need, this will do the trick. Realizing you can manually alter EFQ queries opens a huge world of opportunities for creating very powerful queries while still keeping the interface clean and Drupal.

Let me know if you have any questions or issues.

Edit: I actually found a little more performant way of doing this and changed my code accordingly.

Tags:

Entities

7