EntityFieldQuery kullanarak tüm kullanıcıları belirli roller ile al


35

Bunun kolay bir görev olduğunu düşünmüştüm, ancak bunun için bir Drupal yöntemi yok gibi görünüyor. Bunun için kullanmak zorunda olduğumu bildiğim kadarıyla geldim EntityFieldQuery- çünkü API şartlarının user_load_multiple()kullanımdan kaldırıldığını söyledi .

Bu yüzden bunu denedim:

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

  $result = $query->execute();

Yine de bu var:

PDOException: SQLSTATE [42S22]: Sütun bulunamadı: 1054 Bilinmeyen sütun 'users.rid' in 'where cümlesi': SELECT users.uid AS entity_id,: entity_type entity_type AS: {null AS revision_id NEREDE (users.rid =: db_condition_placeholder_0); Dizi ([: db_condition_placeholder_0] => 3 [: entity_type] => kullanıcı [: bundle] => kullanıcı) EntityFieldQuery-> execute () içinde

Bu yüzden ilk düşüncem, users_roles-Tablo ve benzeri şeylerle katılmak zorunda kalacağım , ama bu kopyalara neden olacaktı .

Nasıl yapılacağı hakkında fikri olan var mı?


user_load_multiple () aynı zamanda bir no-go. Umarım bunu D8'de yapmanın bir yolunu buluruz.
Dalin

EntityFieldQueryExtra sanal alanımı izin verdiği şekilde kullanabilirsiniz ->propertyCondition('rid', array(1, 2, 3));
mikeytown2

Yanıtlar:


32

Dürüst olmak gerekirse, bunun nasıl başarılacağı hakkında hiçbir fikrim yok. EntityFieldQuery'nin nasıl kullanılacağına dair iyi örnekler bulmak zor. Kimse henüz bu soruyu cevaplamadığından ve ben de size yardımcı olmaya çalışacağım çözümle ilgileniyorum. Aşağıda açıklanan en iyi tahminim.

Akılda tutulması gereken bir şey: roller kullanıcılardan farklı bir tabloda saklanır. UserController :: attachLoad kullanılarak eklenirler .

Bir EntityFieldQuery ile kullanabileceğiniz üç farklı koşul var gibi görünüyor:

  1. entityCondition ( Özelliğe özgü koşullar: 'entity_type', 'bundle', 'revision_id' veya 'entity_id')
  2. fieldCondition (Alan API'sı tarafından korunan alanların koşulları)
  3. propertyCondition (Varlık özelliklerine ilişkin koşullar)

En mantıksal seçenek (varsa) bir propertyCondition kullanmak olacaktır, ancak UserController :: attachLoad'da kullanıcıya roller eklendikçe , EntityFieldQuery'nin buna erişimi olduğunu sanmıyorum. EntityFieldQuery, yalnızca hook_schema () içinde tanımlanan şema kullandığını düşünüyorum.

Bu bana ulaşmaya çalıştığın şeyin imkansız olduğuna inanmamı sağlıyor. Bir geçici çözüm, tüm kullanıcı arabirimlerini normal bir sorguyla elde etmek olacaktır:

Şu an Drupal’a erişimim yok, bu yüzden aşağıdaki kod kapalı olabilir. Eminim birileri hataları düzeltecektir.

// 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 ile istediğinizi elde etmek mümkünse, aydınlanacağım.


1
Evet, böyle yürüdüm. Ancak bunu EFQ ile nasıl başaracağımı çok merak ediyorum ve soruyu açık bırakacağım. EFQ'ların uzun vadede benim için tam olarak yerini alabilecekleri için henüz yeterince belgelenmemiş olması bir üzücü.
Nils Riedemann,

EFQ yalnızca varlık tablosunun bir parçası olan bir işletmenin temel özelliklerini destekler. Bir kullanıcının rolleri varlık sistemine hiçbir şekilde maruz kalmaz.
Berdir

1
Ayrıca, optimizasyon ipucu: $ uids initalization ve foreach ile değiştirebilirsiniz $uids = $result->fetchColumn().
Berdir

Berdir :)
Bart

19

Hile yapmalı

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

Bu cevabın neden bu kadar az oy aldığını merak ediyorum. Bu en iyi çözümdür. Yine $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');de bilinmeyen sütunlara 'uid' neden olabileceğini not etmeliyim . Bu durumda eklemek zorundayız $query->propertyCondition('uid', 0, '!='), bu yüzden kullanıcılar tablosu sorguda olacak.
Elaman

1
Kesinlikle bu kabul edilen cevap olmalıdır.
Frank Robert Anderson,

16

Bunu yapmanın bir yolu var aslında. Kalbinde, EntityFieldQuery (EFQ) sadece sorgu değiştirme kancasıyla değiştirilebilen bir veritabanı sorgusudur.

Mümkün olan en basit örnek:

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

Bununla birlikte, daha fazla kodlama gerektirecek birkaç küçük uyarı var. Örneğin, EFQ’daki bir alan koşulu olarak mevcut olan tek koşulun bakımında, kullanıcılar bazında bulunamazlar; bu nedenle, kullanıcıları rol ve tek bir alan koşulu olarak alırken, aynı zamanda Kullanıcılar tablosu katıldı ve eğer değilse, biraz sıkıcı bir işlemdir, elle katılmak.

Ama senin ihtiyacın için, bu işe yarayacak. EFQ sorgularını manuel olarak değiştirebildiğinizi fark etmek, arayüzü hala temiz ve Drupal'ı tutarken çok güçlü sorgular oluşturmak için çok büyük fırsatlar dünyasını açar.

Herhangi bir sorunuz veya probleminiz olursa bana bildirin.

Düzenleme: Aslında bunu yapmanın biraz daha performanslı bir yolunu buldum ve kodumu buna göre değiştirdim.


Temel gereksinimi "kesmenin" bir yolu her zaman $ query-> propertyCondition ('uid', 0, '! =') Gibi sahte bir özellik koşulu eklemektir; sorguya. Bu, taban tabanının bağlanmaya zorlar, ancak EFQ'nun tek bir alan durumunda taban tabanını terk etmesinin nedeni olarak performansı biraz zorlaştırır. Şart, alan veri tablosunu sorgulamanın daha hızlı olmasıdır.
Tommi Forsström

2
Bunun iddia edildiği gibi mümkün olan en basit örnek olduğunu sanmıyorum. Alt sorguyu iki ortak lehine ve bir şartı doğrudan çözemez $querymisin? Ve ayrıca mymodule_get_users_by_rolenamesorgunun sonucunu döndürmemek, ancak anahtarı kaldırmak için değiştirmeyi önerebilirim '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;

5

Rolünüz için kullanıcı kimliklerini aldıktan sonra, örneğin, fieldCondition () veya başka bir yöntem kullanmak istediğiniz için, örneğin, EntityFieldQuery () yöntemini kullanabilirsiniz.

Yukarıdaki örnekte $ uids dizisini kullanarak:

$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 bir birleştirme yöntemi olsaydı daha iyi olurdu.


1
Bu rolle birkaç düzineden fazla kullanıcınız varsa, bu EFQ çok kötü bir performansa sahip olacaktır.
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);
}

Bence bu muhtemelen en etkili cevap. Hafif bir gelişme $rolenameolarak, çağrıda kullanılacak bir parametre ekler user_role_load_by_nameve user_role_load_by_nameyanlış dönmemesini sağlamak için bir kontrol eklerdim.
stefgosselin

4

Tamam Bu eski bir tanesi ama böyle bir şey yapabilirsin:

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

Fikrimi değiştirdim, bu en iyi cevap. Bunun dışında, bir noktalı virgül eksik ve $ my_role_id tanımsız.
Frank Robert Anderson,

2

Oyuna biraz geç kaldım, ancak OP'nin istediği şeyin bu olduğunu düşünüyorum. örneğin hiçbir sql, hepsi drupal. Umarım diğerleri onu yararlı bulacaktır. Bunun için arama beni buraya getirdi ve işte bunu buldum.

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

Bu rolle birkaç düzineden fazla kullanıcınız varsa, bu EFQ çok kötü bir performansa sahip olacaktır.
Dalin

@Dalin, ilk ehliyet verildiğinde (yani hiçbir sql, hepsi drupal değil), daha iyi performans elde edecek bir yöntem olarak ne önerirsiniz?
Altın

1
Hey Gold! Db_select () koşulu, $ rolünün bir dize olmasını bekliyor gibi görünüyor ve bir tamsayı geçirirseniz, $ role_obj-> rid komutuna başvururken ayarlanmayan $ role_obj üzerinde yakalanır. EDIT: Cevapları düzenleyebilirim, woo.
Chris Burgess,

0

Bu gerçekten daha eski bir konudur ve birçok iyi yaklaşım buldum.

@Alf tarafından sağlanan çözümü biraz geliştirebilirim, çünkü join ile bir sorgulamanın en iyi yaklaşım olduğunu düşünüyorum. Ancak, fonksiyonlarımın parametrelere sahip olmalarını ve sabitlere dayanmamasını da seviyorum. Yani burada gidiyor:

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'yi nasıl kullanacağınıza dair iyi açıklamalar örnekleri burada bulabilirsiniz:

Ancak Berdir'in dediği gibi "EFQ yalnızca varlık tablosunun bir parçası olan bir işletmenin temel özelliklerini destekler. Bir kullanıcının rolleri varlık sistemine hiçbir şekilde maruz kalmaz."


-2

Bunun basitleştirilebileceğinden şüphem yok. Bu drupal 8 için olsa yapılabilir:

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

Hepsini yüklemek yerine doğrudan kullanıcı postalarını toplamak için sorguyu kullanmak daha
iyidir

bu çözüm bu cevaplardan birinde mi?
Matoeil,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.