Dapatkan semua pengguna dengan peran tertentu menggunakan EntityFieldQuery

35

Saya pikir ini adalah tugas yang mudah, namun sepertinya tidak ada metode Drupal untuk ini. Saya datang sejauh mengetahui saya harus menggunakan EntityFieldQueryuntuk ini - karena API mengatakan kondisi untuk user_load_multiple()sudah usang.

Jadi saya mencoba ini:

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

  $result = $query->execute();

Namun saya mendapatkan ini:

PDOException: SQLSTATE [42S22]: Kolom tidak ditemukan: 1054 Kolom tidak dikenal 'users.rid' di 'di mana klausa': SELECT users.uid AS entity_id,: entity_type AS entity_type, NULL AS revision_id,: bundel AS bundel DARI {pengguna} pengguna WHERE (users.rid =: db_condition_placeholder_0); Array ([: db_condition_placeholder_0] => 3 [: entity_type] => pengguna [: bundle] => pengguna) di EntityFieldQuery-> execute ()

Jadi pikiran pertama saya adalah, bahwa saya harus bergabung dengan users_roles-Tabel dan seterusnya, tetapi itu akan menyebabkan duplikat.

Adakah yang punya ide bagaimana melakukannya?

Nils Riedemann
sumber
user_load_multiple () juga tidak bisa digunakan. Semoga kita memiliki cara untuk melakukan ini di D8.
Dalin
Anda dapat menggunakan kotak pasir EntityFieldQueryExtra saya karena memungkinkan untuk ->propertyCondition('rid', array(1, 2, 3));
mikeytown2

Jawaban:

32

Sejujurnya, saya tidak tahu bagaimana mencapai ini. Contoh bagus tentang cara menggunakan EntityFieldQuery sulit ditemukan. Karena belum ada yang menjawab pertanyaan ini dan saya juga tertarik dengan solusi, saya akan mencoba membantu Anda. Dijelaskan di bawah ini adalah tebakan terbaik saya.

Satu hal yang perlu diingat: peran disimpan dalam tabel yang berbeda dari pengguna. Mereka ditambahkan menggunakan UserController :: attachLoad .

Tampaknya ada tiga kondisi berbeda yang bisa Anda gunakan dengan EntityFieldQuery:

  1. entitasCondition ( Persyaratan khusus entitas: 'entity_type', 'bundle', 'revision_id' atau 'entitas_id')
  2. fieldCondition (Ketentuan pada bidang yang dikelola oleh API Bidang)
  3. propertyCondition (Ketentuan tentang properti entitas)

Opsi paling logis (jika ada) adalah menggunakan propertiCondition, tetapi karena peran ditambahkan ke pengguna di UserController :: attachLoad Saya tidak berpikir EntityFieldQuery memiliki akses ke sana. Saya pikir EntityFieldQuery hanya menggunakan skema yang ditentukan dalam hook_schema ().

Ini membuat saya percaya bahwa apa yang Anda coba capai tidak mungkin. Solusinya adalah untuk mendapatkan semua cairan dengan permintaan normal:

Saya tidak memiliki akses ke Drupal sekarang, jadi kode di bawah ini mungkin tidak aktif. Saya yakin seseorang akan mengedit kesalahan.

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

Jika mungkin untuk mencapai apa yang Anda inginkan dengan EntityFieldQuery, saya akan tercerahkan.

Bart
sumber
1
Yup, ini adalah cara saya pergi. Namun saya sangat ingin tahu bagaimana mencapainya dengan EFQ dan akan membiarkan pertanyaan terbuka. Sangat menyedihkan bahwa EFQ belum terdokumentasi dengan baik karena mereka mungkin akan sepenuhnya menggantikan Tampilan untuk saya dalam jangka panjang.
Nils Riedemann
EFQ hanya mendukung properti dasar suatu entitas, yang merupakan bagian dari tabel entitas. Peran pengguna tidak terpapar dengan cara apa pun ke sistem entitas.
Berdir
1
Juga, petunjuk pengoptimalan: Anda dapat mengganti inisialisasi $ uids dan foreach dengan $uids = $result->fetchColumn().
Berdir
Jangan ragu untuk mengedit kode Berdir :)
Bart
19

Seharusnya melakukan trik

  $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
sumber
Saya bertanya-tanya mengapa jawaban ini memiliki sedikit suara. Itu adalah solusi terbaik. Padahal saya harus perhatikan itu $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');bisa menyebabkan kolom 'uid' tidak dikenal. Dalam hal ini kita perlu menambahkan $query->propertyCondition('uid', 0, '!='), sehingga tabel pengguna akan berada dalam kueri.
Elaman
1
Tentunya ini adalah jawaban yang diterima.
Frank Robert Anderson
16

Sebenarnya ada cara untuk melakukan ini. Dalam intinya, EntityFieldQuery (EFQ) hanyalah kueri basis data yang dapat diubah dengan kueri alter kait.

Contoh sederhananya:

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

Namun ada beberapa peringatan kecil dengan ini yang akan memerlukan beberapa pengkodean lagi. Misalnya dalam menangani satu-satunya kondisi yang ada di EFQ menjadi fieldCondition, basis pengguna tidak akan ada, jadi ketika Anda menjemput pengguna berdasarkan peran dan satu kondisi lapangan, Anda juga harus memastikan bahwa tabel pengguna bergabung dan jika tidak, bergabung secara manual, yang merupakan sedikit proses yang membosankan.

Tetapi untuk kebutuhan Anda, ini akan membantu. Menyadari bahwa Anda dapat secara manual mengubah kueri EFQ membuka peluang besar dunia untuk membuat kueri yang sangat kuat sambil tetap menjaga antarmuka tetap bersih dan Drupal.

Beri tahu saya jika Anda memiliki pertanyaan atau masalah.

Sunting: Saya sebenarnya menemukan cara yang lebih banyak melakukan hal ini dan mengubah kode saya.

Tommi Forsström
sumber
Salah satu cara untuk "meretas" persyaratan dasar adalah dengan selalu menambahkan propertikondisi palsu seperti $ query-> propertyCondition ('uid', 0, '! ='); ke kueri. Itu memaksa alas-alas untuk ditautkan, tetapi memang menghambat kinerja hanya dengan sedikit karena alasan EFQ menghilangkan alas-alas dalam kasus fieldCondition tunggal adalah bahwa itu cara yang lebih cepat untuk hanya kueri tabel data bidang.
Tommi Forsström
2
Saya tidak berpikir ini adalah contoh yang paling sederhana, seperti yang diklaim. Tidak bisakah Anda meninggalkan subquery yang mendukung dua gabungan dan satu syarat langsung $query? Dan saya mungkin juga menyarankan mymodule_get_users_by_rolenameuntuk mengubah agar tidak mengembalikan hasil query itu sendiri, tetapi untuk dereference kuncinya 'user'.
Peter Taylor
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
sumber
5

Anda masih dapat menggunakan EntityFieldQuery () setelah Anda mendapatkan id pengguna untuk peran Anda, jika Anda mau, misalnya karena Anda ingin pengguna fieldCondition () atau metode lain.

Menggunakan array $ uids dalam contoh di atas:

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

Akan lebih baik jika EntityFieldQuery memiliki metode bergabung.

JUPITER
sumber
1
Jika Anda memiliki lebih dari beberapa lusin pengguna dengan peran ini, EFQ ini akan memiliki kinerja yang buruk.
Dalin
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);
}
ibit
sumber
Saya pikir ini mungkin jawaban yang paling efisien. Sebagai sedikit perbaikan saya akan menambahkan parameter $rolenameyang akan digunakan dalam panggilan ke user_role_load_by_namedan cek untuk memastikan user_role_load_by_nametidak mengembalikan false.
stefgosselin
4

OK Ini sudah lama, tetapi Anda bisa melakukan sesuatu seperti ini:

$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
sumber
Saya berubah pikiran, ini adalah jawaban terbaik. Kecuali, Anda kehilangan titik koma dan $ my_role_id tidak terdefinisi.
Frank Robert Anderson
2

Ini sedikit terlambat untuk permainan, tapi saya pikir itulah yang diminta OP. misal tidak ada sql, semua drupal. Semoga orang lain akan merasakan manfaatnya. Pencarian ini membawa saya ke sini dan inilah yang saya hasilkan.

    /**
     * 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;
    }
Emas
sumber
Jika Anda memiliki lebih dari beberapa lusin pengguna dengan peran ini, EFQ ini akan memiliki kinerja yang buruk.
Dalin
@ Dalin, diberi kualifikasi awal, (yaitu tidak ada sql, semua drupal), apa yang akan Anda sarankan sebagai metode yang akan mendapatkan kinerja yang lebih baik?
Emas
1
Hai emas! Sepertinya kondisi db_select () mengharapkan $ role menjadi string, dan jika Anda memasukkan bilangan bulat, itu akan tertangkap pada $ role_obj tidak disetel ketika merujuk $ role_obj-> rid. EDIT: Saya bisa mengedit jawaban, woo.
Chris Burgess
0

Ini memang topik yang lebih tua dan saya menemukan banyak pendekatan yang bagus.

Saya akan meningkatkan sedikit pada solusi yang disediakan oleh @alf, karena saya pikir satu permintaan dengan bergabung adalah pendekatan terbaik. Namun saya juga suka fungsi saya memiliki parameter dan tidak bergantung pada konstanta. Jadi begini:

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
sumber
-1

Anda dapat menemukan contoh penjelasan yang baik tentang cara menggunakan EntityFieldQuery di sini:

tetapi seperti yang dikatakan Berdir untuk saat ini "EFQ hanya mendukung properti dasar suatu entitas, yang merupakan bagian dari tabel entitas. Peran pengguna tidak terpapar dengan cara apa pun pada sistem entitas."

k_zoltan
sumber
-2

Saya tidak ragu ini bisa disederhanakan. Ini dapat dilakukan meskipun untuk 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
sumber
Lebih baik menggunakan kueri secara langsung untuk mengumpulkan email pengguna, daripada memuat semuanya
Codium
Apakah solusi ini dalam salah satu jawaban itu?
Matoeil