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 'where句'の不明な列 'users.rid':SELECT users.uid AS entity_id、:entity_type AS entity_type、NULL AS revision_id、:bundle AS bundle FROM {users} users WHERE(users.rid =:db_condition_placeholder_0); EntityFieldQuery-> execute()の配列([:db_condition_placeholder_0] => 3 [:entity_type] => user [:bundle] => user)

したがって、最初に考えたのは、users_roles-Tableなどで参加する必要があるということでした、重複することになります。

誰もそれを行う方法を知っていますか?


user_load_multiple()も禁止です。D8でこれを行う方法があればいいのですが。
ダリン

可能であればEntityFieldQueryExtraサンドボックスを使用できます->propertyCondition('rid', array(1, 2, 3))
mikeytown2 14

回答:


32

正直に言うと、これをどのように達成するかわからない。EntityFieldQueryの使用方法の良い例は見つけにくいです。まだ誰もこの質問に答えていないので、解決策にも興味がありますので、お手伝いします。以下に説明するのは私の最良の推測です。

心に留めておくべきこと:ロールはユーザーとは異なるテーブルに保存されます。それらはUserController :: attachLoadを使用して追加されます。

EntityFieldQueryで使用できる3つの異なる条件があるようです。

  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が長い目で見れば完全にViewsに取って代わるかもしれないので、EFQがまだ十分に文書化されていないのは気の毒です。
ニルスリーデマン

EFQは、エンティティテーブルの一部であるエンティティの基本的なプロパティのみをサポートします。ユーザーのロールは、エンティティシステムには一切公開されません。
ベルディール

1
また、最適化のヒント:$ uids initalizationとforeachをに置き換えることができ$uids = $result->fetchColumn()ます。
ベルディール

コードBerdirを自由に編集してください:)
バート

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, '!=')、usersテーブルがクエリに含まれるように、を追加する必要があります。
エラマン

1
間違いなくこれは受け入れられた答えであるべきです。
フランクロバートアンダーソン

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である場合、users-basetableは存在しないため、ロールと単一のfieldConditionでユーザーを取得する場合は、 usersテーブルは結合され、結合されていない場合は手動で結合されますが、これは少し面倒なプロセスです。

しかし、あなたの必要に応じて、これはトリックを行います。EFQクエリを手動で変更できることに気づくと、非常に強力なクエリを作成しつつ、インターフェイスをクリーンでDrupalに保つ機会が広がります。

ご質問や問題がある場合はお知らせください。

編集:私は実際にこれを行うもう少しパフォーマンスの高い方法を見つけ、それに応じてコードを変更しました。


ベーステーブルの要件を「ハッキング」する1つの方法は、$ query-> propertyCondition( 'uid'、0、 '!=');のような偽のプロパティ条件を常に追加することです。クエリに。これにより、ベーステーブルが強制的にリンクされますが、単一のfieldConditionの場合にEFQがベーステーブルを除外する理由は、フィールドデータテーブルを照会する方が高速であるためです。
トミフォルストローム

2
主張されているように、これは可能な限り単純な例ではないと思います。サブクエリを捨てて、2つの結合と1つの条件を直接オンにすることはできません$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に結合メソッドがあればもっといいでしょう。


1
この役割を持つユーザーが数十人以上いる場合、このEFQのパフォーマンスはひどくなります。
ダリン

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_namefalseを返さないことを確認するチェックを追加します。
-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が未定義です。
フランクロバートアンダーソン

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、最初の資格(つまり、SQLなし、すべてdrupal)が与えられたら、パフォーマンスを向上させる方法として何を提案しますか?
ゴールド14

1
ちょっとゴールド!db_select()条件は、$ roleが文字列であると想定しているように見えます。整数を渡すと、$ role_obj-> ridを参照するときに、$ role_objが設定されていないことがわかります。編集:答えを編集できます。
クリスバージェス

0

これは確かに古いトピックであり、多くの優れたアプローチが見つかりました。

私は、@ alfが提供するソリューションを少し改善します。なぜなら、結合を伴う1つのクエリが最良のアプローチだと思うからです。ただし、関数がパラメーターを持ち、定数に依存しないことも気に入っています。だからここに行く:

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

この解決策はそれらの答えの1つですか?
マトエイユ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.