Nhận tất cả người dùng với các vai trò cụ thể bằng EntityFieldQuery


35

Tôi nghĩ rằng đây là một nhiệm vụ dễ dàng, nhưng dường như không có phương pháp Drupal nào cho việc này. Tôi đã đi xa đến mức biết rằng tôi phải sử dụng EntityFieldQuerycho việc này - vì API cho biết các điều kiện user_load_multiple()không được chấp nhận.

Vì vậy, tôi đã thử điều này:

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

  $result = $query->execute();

Nhưng tôi đã nhận được điều này:

PDOException: SQLSTATE [42S22]: Không tìm thấy cột: 1054 Cột không xác định 'users.rid' trong 'mệnh đề where': SELECT users.uid AS entity_id ,: entity_type AS entity_type, NULL AS revision_id ,: bundle AS bundle TỪ {users} WHERE (users.rid =: db_condition_placeholder_0); Mảng ([: db_condition_placeholder_0] => 3 [: entity_type] => user [: bundle] => user) trong EntityFieldQuery-> exec ()

Vì vậy, suy nghĩ đầu tiên của tôi là, tôi phải tham gia với users_roles-Table và cứ thế, nhưng điều đó sẽ dẫn đến sự trùng lặp.

Có ai có một ý tưởng làm thế nào để làm điều đó?


user_load_mult Môn () cũng là không có. Hy vọng rằng chúng ta sẽ có cách để làm điều này trong D8.
Dalin

Bạn có thể sử dụng của tôi hộp cát EntityFieldQueryExtra vì nó cho phép ->propertyCondition('rid', array(1, 2, 3));
mikeytown2

Câu trả lời:


32

Thành thật mà nói, tôi không có ý tưởng làm thế nào để đạt được điều này. Các ví dụ hay về cách sử dụng EntityFieldQuery rất khó tìm. Vì chưa có ai trả lời câu hỏi này và tôi cũng quan tâm đến giải pháp tôi sẽ cố gắng giúp bạn. Mô tả dưới đây là dự đoán tốt nhất của tôi.

Một điều cần lưu ý: vai trò được lưu trữ trong một bảng khác với người dùng. Chúng được thêm vào bằng UserControll :: AttachLoad .

Dường như có ba điều kiện khác nhau mà bạn có thể sử dụng với EntityFieldQuery:

  1. entityCondition (Các điều kiện cụ thể của thực thể: 'entity_type', 'bundle', 'revision_id' hoặc 'entity_id')
  2. fieldCondition (Điều kiện trên các trường được duy trì bởi API trường)
  3. propertyCondition (Điều kiện về thuộc tính thực thể)

Tùy chọn hợp lý nhất (nếu có) sẽ là sử dụng propertyCondition, nhưng khi các vai trò được thêm vào người dùng trong UserControll :: AttachLoad Tôi không nghĩ EntityFieldQuery có quyền truy cập vào nó. Tôi nghĩ EntityFieldQuery chỉ sử dụng lược đồ được định nghĩa trong hook_schema ().

Điều này khiến tôi tin rằng những gì bạn đang cố gắng đạt được là không thể. Một cách giải quyết khác là lấy tất cả các uids bằng một truy vấn bình thường:

Tôi không có quyền truy cập vào Drupal ngay bây giờ, vì vậy mã dưới đây có thể bị tắt. Tôi chắc chắn ai đó sẽ sửa lỗi.

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

Nếu có thể đạt được những gì bạn muốn với EntityFieldQuery, tôi sẽ được khai sáng.


1
Yup, đây là con đường tôi đã đi. Tuy nhiên, tôi rất tò mò làm thế nào để đạt được điều này với EFQ và sẽ bỏ ngỏ câu hỏi. Thật đáng tiếc khi các EFQ chưa được ghi chép đầy đủ vì về lâu dài họ có thể thay thế Lượt xem cho tôi.
Nils Riedemann

EFQ chỉ hỗ trợ các thuộc tính cơ bản của một thực thể, là một phần của bảng thực thể. Vai trò của người dùng không được bộc lộ dưới bất kỳ hình thức nào đối với hệ thống thực thể.
Berdir

1
Ngoài ra, gợi ý tối ưu hóa: Bạn có thể thay thế $ uids initalization và foreach bằng $uids = $result->fetchColumn().
Berdir

Vui lòng chỉnh sửa mã Berdir :)
Bart

19

Nó sẽ làm các 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);
}

Tôi tự hỏi tại sao câu trả lời này có rất ít phiếu bầu. Đó là giải pháp tốt nhất. Mặc dù tôi nên lưu ý rằng $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');có thể gây ra cột 'uid' không xác định. Trong trường hợp đó chúng ta cần thêm $query->propertyCondition('uid', 0, '!='), vì vậy bảng người dùng sẽ nằm trong truy vấn.
Elaman

1
Chắc chắn đây sẽ là câu trả lời được chấp nhận.
Frank Robert Anderson

16

Thực tế có một cách để làm điều này. Về bản chất, EntityFieldQuery (EFQ) chỉ là một truy vấn cơ sở dữ liệu có thể được thay đổi bằng các móc thay đổi truy vấn.

Ví dụ đơn giản nhất có thể:

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

Tuy nhiên, có một vài cảnh báo nhỏ với điều này sẽ yêu cầu thêm một số mã hóa. Ví dụ: trong việc chăm sóc điều kiện duy nhất có trong EFQ là FieldCondition, cơ sở người dùng sẽ không có mặt, vì vậy khi bạn tìm nạp người dùng theo vai trò và một trường duy nhất, bạn cũng cần đảm bảo bảng người dùng được tham gia và nếu không, hãy tham gia bằng tay, đây là một quá trình tẻ nhạt.

Nhưng đối với nhu cầu của bạn, điều này sẽ thực hiện các mẹo. Nhận ra bạn có thể thay đổi thủ công các truy vấn EFQ mở ra một thế giới cơ hội lớn để tạo các truy vấn rất mạnh trong khi vẫn giữ giao diện sạch và Drupal.

Hãy cho tôi biết nếu bạn có bất kỳ câu hỏi hoặc vấn đề.

Chỉnh sửa: Tôi thực sự tìm thấy một cách hiệu quả hơn một chút để làm điều này và thay đổi mã của tôi cho phù hợp.


Một cách để "hack" yêu cầu cơ bản là luôn luôn thêm một tài sản không có thật như $ query-> propertyCondition ('uid', 0, '! ='); để truy vấn. Điều đó buộc các cơ sở có thể được liên kết, nhưng gây cản trở hiệu suất chỉ một chút vì lý do EFQ rời khỏi cơ sở trong trường hợp của một FieldCondition duy nhất là cách truy vấn bảng dữ liệu trường nhanh hơn.
Tommi Forsström

2
Tôi không nghĩ rằng đây là ví dụ đơn giản nhất có thể, như đã tuyên bố. Bạn có thể bỏ qua truy vấn phụ có lợi cho hai liên kết và một điều kiện trực tiếp $querykhông? Và tôi cũng có thể đề nghị thay đổi mymodule_get_users_by_rolenameđể không trả về kết quả của chính truy vấn, nhưng để hủy đăng ký khóa '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

Bạn vẫn có thể sử dụng EntityFieldQuery () sau khi bạn nhận được id người dùng cho vai trò của mình, nếu bạn muốn, ví dụ vì bạn muốn người dùng fieldCondition () hoặc phương thức khác.

Sử dụng mảng $ uids trong ví dụ trên:

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

Sẽ tốt hơn nếu EntityFieldQuery có phương thức nối.


1
Nếu bạn có hơn vài chục người dùng với vai trò này, thì EFQ này sẽ có hiệu suất khủng khiếp.
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);
}

Tôi nghĩ rằng đây có lẽ là câu trả lời hiệu quả nhất. Để cải thiện một chút, tôi sẽ thêm một tham số$rolename sẽ được sử dụng trong cuộc gọi đến user_role_load_by_namevà kiểm tra để đảm bảo user_role_load_by_namekhông trả về sai.
stefgosselin

4

OK Đây là một cái cũ, nhưng bạn có thể làm một cái gì đó như thế này:

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

Tôi đã thay đổi suy nghĩ đây là câu trả lời tốt nhất. Ngoại trừ, bạn đang thiếu dấu chấm phẩy và $ my_role_id không được xác định.
Frank Robert Anderson

2

Đây là một trò chơi hơi muộn, nhưng tôi nghĩ đó là những gì OP yêu cầu. ví dụ không có sql, tất cả drupal. Hy vọng những người khác sẽ tìm thấy nó hữu ích. Việc tìm kiếm này đã dẫn tôi đến đây và đây là những gì tôi nghĩ ra.

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

Nếu bạn có hơn vài chục người dùng với vai trò này, thì EFQ này sẽ có hiệu suất khủng khiếp.
Dalin

@Dalin, với trình độ ban đầu, (tức là không có sql, tất cả drupal), bạn sẽ đề xuất phương pháp nào để có hiệu suất tốt hơn?
Vàng

1
Này vàng! Có vẻ như điều kiện db_select () dự kiến ​​$ vai trò là một chuỗi và nếu bạn chuyển vào một số nguyên, nó sẽ bị bắt khi $ role_obj không được đặt khi tham chiếu $ role_obj-> thoát. EDIT: Tôi có thể chỉnh sửa câu trả lời, woo.
Chris Burgess

0

Đây thực sự là một chủ đề cũ hơn và tôi đã tìm thấy rất nhiều cách tiếp cận tốt.

Tôi sẽ cải thiện một chút về giải pháp được cung cấp bởi @alf, vì tôi nghĩ một truy vấn có tham gia là cách tiếp cận tốt nhất. Tuy nhiên tôi cũng thích các hàm của mình có các tham số và không phụ thuộc vào hằng số. Vì vậy, ở đây nó đi:

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

Bạn có thể tìm thấy một ví dụ giải thích hay về cách sử dụng EntityFieldQuery tại đây:

nhưng như Berdir đã nói hiện tại "EFQ chỉ hỗ trợ các thuộc tính cơ bản của một thực thể, là một phần của bảng thực thể. Vai trò của người dùng không được thể hiện dưới bất kỳ hình thức nào đối với hệ thống thực thể."


-2

Tôi không có nghi ngờ điều này có thể được đơn giản hóa. Điều này có thể được thực hiện mặc dù cho 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);

Tốt hơn là sử dụng truy vấn trực tiếp để thu thập thư người dùng, thay vì tải tất cả
Codium

Là giải pháp này trong một trong những câu trả lời?
Matoeil
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.