Отримайте всіх користувачів із певними ролями за допомогою EntityFieldQuery


35

Я вважав, що це легке завдання, але, схоже, не існує методу Друпала для цього. Я прийшов настільки, наскільки знав, що я повинен використовувати 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 Невідомий стовпець 'users.rid' в 'де пункт': SELECT users.uid AS entit_id,: entit_type AS object_type, NULL AS revision_id,: bundle AS bundle FROM {users} користувачів ДЕЙ (users.rid =: db_condition_placeholder_0); Масив ([: db_condition_placeholder_0] => 3 [: entit_type] => user [: bundle] => user) в EntityFieldQuery-> Execute ()

Так що моя перша думка була, що я мав би приєднатися до users_roles-Столи і так далі, але це буде призводити до дублям.

Хтось має уявлення, як це зробити?


user_load_multiple () - також не використовується. Сподіваємось, у нас буде спосіб зробити це у D8.
Далін

Ви можете використовувати мій пісочник EntityFieldQueryExtra, як це дозволяє ->propertyCondition('rid', array(1, 2, 3));
mikeytown2

Відповіді:


32

Якщо чесно, я не маю уявлення про те, як цього досягти. Хороші приклади використання EntityFieldQuery важко знайти. Оскільки ніхто ще не відповів на це питання, і я також зацікавлений у вирішенні, я спробую допомогти вам. Описане нижче - моя найкраща здогадка.

Що потрібно пам’ятати: ролі зберігаються в іншій таблиці, ніж користувачі. Вони додаються за допомогою UserController :: attachLoad .

Здається, є три різні умови, які можна використовувати з EntityFieldQuery:

  1. entitCondition (специфічні для суб'єкта умови: 'type_type', 'bundle', 'revision_id' або 'entit_id')
  2. fieldCondition (Умови щодо полів, що підтримуються API Field)
  3. propertyCondition (Умови щодо властивостей юридичної особи)

Найбільш логічним варіантом (якщо такий є) було б використання властивостіCoCotion, але оскільки ролі додаються користувачеві в UserController :: attachLoad, я не думаю, що EntityFieldQuery має доступ до нього. Я думаю, що EntityFieldQuery просто використовує схему, визначену в кучку_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 ще не настільки добре задокументовані, тому що вони можуть повністю замінити Погляди для мене в перспективі.
Нілс Рідеманн

EFQ підтримує лише основні властивості сутності, які є частиною таблиці сутності. Ролі користувача жодним чином не піддаються впливу системи сутності.
Бердір

1
Також підказка щодо оптимізації: Ви можете замінити ініталізацію $ uids та foreach на $uids = $result->fetchColumn().
Бердір

Не соромтесь редагувати код Бердір :)
Барт

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, '!='), тому таблиця користувачів буде в запиті.
Еламан

1
Однозначно це має бути прийнята відповідь.
Френк Роберт Андерсон

16

Насправді існує спосіб це зробити. За своєю суттю EntityFieldQuery (EFQ) - це лише запит до бази даних, який можна змінити за допомогою гачок для зміни запитів.

Найпростіший можливий приклад:

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, база даних, що базується на користувачах, не буде присутній, тому, коли ви збираєте користувачів за роллю та одним полемCondition, вам також потрібно буде переконатися, що Таблиця користувачів приєднується, а якщо ні, то вручну приєднуйтесь до неї, що є трохи копітким процесом.

Але для вашої потреби це зробить трюк. Зрозумівши, що ви можете вручну змінити запити EFQ, відкриває величезний світ можливостей для створення дуже потужних запитів, зберігаючи інтерфейс чистим та Drupal.

Повідомте мене, якщо у вас є якісь питання.

Редагувати: я фактично знайшов трохи ефективніший спосіб зробити це і відповідно змінив свій код.


Один із способів "зламати" базову вимогу - завжди додавати помилкове умова властивості, як $ query-> propertyCondition ('uid', 0, '! ='); до запиту. Це змушує пов'язувати базовий базис, але трохи перешкоджає виконанню, оскільки причина, щодо якої EFQ залишає базову таблицю у випадку з одним полемУмови, полягає в тому, що набагато швидше просто запитати таблицю даних про поля.
Tommi Forsström

2
Я не думаю, що це цілком простий можливий приклад, як стверджується. Ви не можете викинути підзапит на користь двох приєднань та однієї умови безпосередньо $query? І я також можу запропонувати змінити, mymodule_get_users_by_rolenameщоб не повертати результат самого запиту, а відкинути ключ '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

Ви все ще можете використовувати EntityFieldQuery (), як тільки ви отримаєте ідентифікатори користувача для своєї ролі, якщо ви хочете, наприклад, тому що ви хочете скористатись полем полеCondition () або іншим методом.

Використання масиву $ 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 мав метод з'єднання.


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 не визначено.
Френк Роберт Андерсон

2

Це трохи пізно до гри, але я думаю, що саме про це просив ОП. наприклад, немає 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, всі друпали), що б ви запропонували як метод, який би мав кращі показники?
Золото

1
Гей золото! Схоже, умова db_select () очікує, що $ роль буде рядком, і якщо ви перейдете в ціле число, воно потрапить на $ role_obj, не встановлену, коли він посилається на $ role_obj-> rid. EDIT: Я можу редагувати відповіді, ву.
Кріс Берджесс

0

Це справді давня тема, і я знайшов багато хороших підходів.

Я б трохи покращив рішення, яке надає @alf, тому що я вважаю, що один запит із приєднанням - найкращий підхід. Однак мені також подобається, що у моїх функцій є параметри і не залежати від постійної. Тож ось що:

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 тут:

  • http://drupal.org/node/1343708 (у цій статті ви знайдете більше корисних ресурсів, навчальних посібників та прикладів)

але так само, як сказав Бердір, як зараз "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

це рішення в одній з цих відповідей?
Матейл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.