รับผู้ใช้ทั้งหมดที่มีบทบาทเฉพาะโดยใช้ 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 คอลัมน์ที่ไม่รู้จัก 'users.rid' ใน 'where clause': SELECT users.uid AS entity_id,: entity_type AS เอนทิตีตี้, NULL AS revision_id,: บันเดิล AS บันเดิลจากผู้ใช้ {ผู้ใช้} WHERE (users.rid =: db_condition_placeholder_0); Array ([: db_condition_placeholder_0] => 3 [: entity_type] => ผู้ใช้ [: บันเดิล] => ผู้ใช้) ใน EntityFieldQuery-> execute ()

ดังนั้นความคิดแรกของฉันคือฉันต้องเข้าร่วมกับusers_roles-Table และต่อไป แต่นั่นจะนำไปสู่การซ้ำซ้อน

ใครบ้างมีความคิดจะทำอย่างไร


user_load_multiple () ก็ไม่ต้องไปด้วย หวังว่าเราจะมีวิธีในการทำเช่นนี้ใน D8
Dalin

คุณสามารถใช้ของฉันEntityFieldQueryExtra Sandbox เป็นมันช่วยให้การ->propertyCondition('rid', array(1, 2, 3));
mikeytown2

คำตอบ:


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 เพียงแค่ใช้ schema ที่กำหนดใน hook_schema ()

สิ่งนี้ทำให้ฉันเชื่อว่าสิ่งที่คุณพยายามทำนั้นเป็นไปไม่ได้ วิธีแก้ปัญหาคือการรับ uids ทั้งหมดด้วยแบบสอบถามปกติ:

ฉันไม่สามารถเข้าถึง 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 ทั้งหมดสำหรับฉันในระยะยาว
Nils Riedemann

EFQ สนับสนุนเฉพาะคุณสมบัติพื้นฐานของเอนทิตีซึ่งเป็นส่วนหนึ่งของตารางเอนทิตี บทบาทของผู้ใช้จะไม่ถูกเปิดเผยต่อระบบเอนทิตี
Berdir

1
นอกจากนี้ยังมีคำแนะนำการเพิ่มประสิทธิภาพ: คุณสามารถเปลี่ยนที่ $ UIDs initalization และ foreach $uids = $result->fetchColumn()ด้วย
Berdir

อย่าลังเลที่จะแก้ไขรหัส 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, '!=')ดังนั้นตารางผู้ใช้จะอยู่ในแบบสอบถาม
Elaman

1
แน่นอนว่านี่ควรเป็นคำตอบที่ได้รับการยอมรับ
Frank Robert Anderson

16

ในความเป็นจริงมีวิธีการทำเช่นนี้ ในหัวใจของมัน EntityFieldQuery (EFQ) เป็นเพียงแบบสอบถามฐานข้อมูลที่สามารถแก้ไขได้ด้วย hooks แก้ไขแบบสอบถาม

ตัวอย่างที่เป็นไปได้ที่ง่ายที่สุด:

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, '! =') เสมอ ไปยังแบบสอบถาม สิ่งนี้บังคับให้ basetable เชื่อมโยง แต่จะขัดขวางประสิทธิภาพการทำงานเพียงเล็กน้อยเนื่องจากเหตุผลที่ EFQ ทำให้ basetable ในกรณีของ fieldCondition เดียวคือวิธีที่เร็วกว่าที่จะสืบค้นตาราง data field
Tommi Forsström

2
ฉันไม่คิดว่านี่เป็นตัวอย่างที่ง่ายที่สุดเท่าที่เป็นไปได้ คุณไม่สามารถคัดเคียวรีย่อยลงในความโปรดปรานของการรวมสองรายการและมีเงื่อนไขเดียวได้$queryหรือไม่? และฉันยังอาจแนะนำการเปลี่ยนแปลงmymodule_get_users_by_rolenameที่จะไม่แสดงผลลัพธ์ของการค้นหาตัวเอง แต่เพื่อ dereference '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

คุณยังคงสามารถใช้ EntityFieldQuery () เมื่อคุณได้รับรหัสผู้ใช้สำหรับบทบาทของคุณถ้าคุณต้องการตัวอย่างเช่นเพราะคุณต้องการ user fieldCondition () หรือวิธีอื่น

การใช้อาร์เรย์ $ 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 นี้จะมีประสิทธิภาพที่แย่มาก
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);
}

ฉันคิดว่านี่อาจเป็นคำตอบที่มีประสิทธิภาพที่สุด เป็นการปรับปรุงเล็กน้อยฉันจะเพิ่มพารามิเตอร์$rolenameที่จะใช้ในการเรียกuser_role_load_by_nameและการตรวจสอบเพื่อให้แน่ใจว่าuser_role_load_by_nameจะไม่ส่งคืนเท็จ
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

@Dalin ได้รับการรับรองเบื้องต้น (เช่น no sql, drupal ทั้งหมด) คุณจะแนะนำวิธีการใดที่จะได้ประสิทธิภาพที่ดีขึ้น
ทองคำ

1
เฮ้โกลด์! ดูเหมือนว่าเงื่อนไข db_select () คาดว่าบทบาท $ จะเป็นสตริงและถ้าคุณผ่านจำนวนเต็มจะได้รับการจับใน $ role_obj ไม่ได้ถูกตั้งค่าเมื่อมันอ้างอิงกำจัด $ role_obj-> แก้ไข: ฉันสามารถแก้ไขคำตอบได้
Chris Burgess

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 (ในบทความนี้คุณจะได้พบกับแหล่งข้อมูลที่มีประโยชน์เพิ่มเติมแบบฝึกหัดและตัวอย่าง)

แต่ดังที่ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.