EntityFieldQuery를 사용하여 특정 역할을 가진 모든 사용자 확보


35

나는 이것이 쉬운 작업이라고 생각했지만 Drupal 방법이없는 것 같습니다. EntityFieldQueryAPI는 조건 user_load_multiple()이 더 이상 사용되지 않기 때문에 이것을 사용해야한다는 것을 알았습니다 .

그래서 나는 이것을 시도했다 :

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

  $result = $query->execute();

그러나 나는 이것을 얻었다.

PDOException : SQLSTATE [42S22] : 열을 찾을 수 없음 : 1054 'where 절'에서 알 수없는 'users.rid'열 : SELECT users.uid AS entity_id, : entity_type AS entity_type, NULL AS edition_id, : bundle AS bundle FROM {users} 사용자 어디에서 (users.rid = : db_condition_placeholder_0); EntityFieldQuery의 배열 ([: db_condition_placeholder_0] => 3 [: entity_type] => user [: bundle] => user)-> execute ()

그래서 나의 첫 번째 생각은 users_roles-Table 등과 합쳐야 한다는 것이 었 습니다. 그러나 그것은 중복으로 이어질 입니다.

누구나 그것을하는 방법을 알고 있습니까?


user_load_multiple ()도 마찬가지입니다. D8에서이 작업을 수행 할 수있는 방법이 있기를 바랍니다.
Dalin

EntityFieldQueryExtra 샌드 박스를 사용할 수 있습니다 ->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는 hook_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가 장기적으로 나를 대신하여 뷰를 완전히 대체 할 수 있기 때문에 아직 잘 문서화되어 있지 않은 것은 미안합니다.
Nils Riedemann

EFQ는 엔터티 테이블의 일부인 엔터티의 기본 속성 만 지원합니다. 사용자의 역할은 어떤 방식 으로든 엔티티 시스템에 노출되지 않습니다.
Berdir

1
또한 최적화 힌트 : $ uids 초기화 및 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, '!=')사용자 테이블이 쿼리 에 추가 되도록 을 추가해야 합니다.
엘라 만

1
확실히 이것은 받아 들일만한 대답이어야합니다.
Frank Robert Anderson

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 인 경우, 사용자 기반 테이블은 존재하지 않으므로 역할 및 단일 fieldCondition으로 사용자를 가져 오는 경우에도 users 테이블이 조인되고 그렇지 않은 경우 수동으로 조인합니다. 이는 약간 지루한 프로세스입니다.

그러나 필요에 따라 트릭을 수행 할 것입니다. 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

예를 들어 fieldCondition () 또는 다른 메소드를 사용하려는 경우와 같이 역할의 사용자 ID를 확보 한 후에도 EntityFieldQuery ()를 계속 사용할 수 있습니다.

위 예제에서 $ 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에 join 메소드가 있으면 더 좋습니다.


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호출에 사용될 매개 변수 를 추가하고 false를 반환하지 user_role_load_by_name않는지 확인합니다 user_role_load_by_name.
stefgosselin

4

OK 이것은 오래된 것이지만 다음과 같이 할 수 있습니다.

$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, 초기 자격 조건 (예 : SQL 없음, 모든 drupal)을 고려할 때 더 나은 성능을 얻을 수있는 방법으로 무엇을 제안 하시겠습니까?
Gold

1
헤이 골드! db_select () 조건은 $ role이 문자열이 될 것으로 예상하고 정수를 전달하면 $ role_obj-> rid를 참조 할 때 설정되지 않은 $ 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를 사용하는 방법에 대한 좋은 설명 예제를 여기에서 찾을 수 있습니다.

그러나 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.