web-dev-qa-db-ja.com

ルール:オブジェクトまたはエンティティID(パフォーマンス)によるエンティティのリストの読み込み

ルール内のエンティティ参照プロパティによってユーザーのリストをロードしようとしています。セカンダリプロパティを更新するために、一致するプロパティを持つ数百または数千のユーザーをロードする必要がある場合があります。最適なパフォーマンスオプションを決定するのに苦労しています。

これが私のユースケースです:

当社のウェブサイト内のユーザーは、会社(Node with Content-Type:companyとして保存)にリンクされている場合があります。この会社のユーザーがメンバーシップを購入すると、company Nodeは、分類用語参照「メンバー」で更新されます。次に、その組織のすべての従業員(読み取り:同じcompanyノードへのエンティティ参照を持つユーザー)が必要です。アカウント内の分類基準参照フィールドを更新して、それらが「メンバー(Corp)」であることを反映するようにします。

私のルールベースのソリューション(注文が最初に全額支払われたときに実行され、会社ノードのそのプロパティのデータ値を設定します...):

ソリューションA(VBOビューのないルール):

  1. プロパティごとにすべてのユーザーエンティティを取得:company、ここでcompany =会社のノード。
  2. ユーザーリストをループします。
  3. エンティティにフィールド「メンバーシップステータス」があることを確認します。
  4. データ値の設定:「メンバーシップのステータス」=「メンバー(会社)」。

ソリューションB(ルール:VBOビューからエンティティのリストを読み込む):

  1. 表示するパラメーターとしてnidを渡します。
  2. 返されたリストをループします。
  3. エンティティにフィールド「メンバーシップステータス」があることを確認します。
  4. データ値の設定:「メンバーシップのステータス」=「メンバー(会社)」。

ソリューションC(ルール:VBOビューからエンティティIDのリストをロードする):

  1. 表示するパラメーターとしてnidを渡します。
  2. 返されたリストをループします。
  3. IDでユーザーエンティティを取得します。
  4. エンティティにフィールド「メンバーシップステータス」があることを確認します。
  5. データ値の設定:「メンバーシップのステータス」=「メンバー(会社)」。

パフォーマンスの影響により、一致するすべてのエンティティをロードするのをためらっています。ただし、Drupalはかなり新しく、影響を受けるすべてのユーザーを一括更新する最も効率的な方法はわかりません。

これらのソリューションのパフォーマンスへの影響を理解するのを手伝ってくれませんか?おそらく、私が検討していないより良いオプションがないのですか?よろしくお願いします!

*この質問は、Drupal 7.を参照しています。

3
Shaun O

ソリューションAを使用すると、パフォーマンスにかなりの影響があるため、「数百または数千のユーザーをロードする」というアプローチはお勧めしません。以下は、さらに別のアプローチ( "better"かどうかを判断するのはあなた次第です)の青写真です。これにより、パフォーマンスリスクを大幅に削減できます...

ステップ1-ビューを調整する

そのビューの結果を最初のXユーザーのみに制限することにより、既存のVBOビューを適合させます。必要に応じてXの値を選択します。Xの値を十分小さくし、後で必要なだけ大きくします。

また、ビューフィルターを調整して、(次の手順で説明するルールコンポーネントによって)既に処理されたユーザーを含まないようにします。

手順2-ルールコンポーネントを作成する

ソリューション「B」または「C」のようにルールアクションを実行するカスタムルールコンポーネントを作成します。 Xの値が十分に小さい場合、これらのソリューション(BまたはC)のいずれかを使用していくつかの実験を行うことができます。つまり、Xの値を増やした場合に何が起こるかを見つけることができます(それに基づいて、どれが最も効果的かを判断します)。処理されるリストは「Xユーザー」(つまり、ビューに含まれているものは前のステップの結果)に限定されていることに注意してください。

ルールコンポーネントに追加のルールアクションを追加して、実際に数秒または数分後に、このルールコンポーネント自体の実行を再スケジュールします(どちらでもかまいません)。そして、「会社の任意のユーザーがメンバーシップを購入したとき」とトリガーされるカスタムルールを介して、このルールコンポーネントの最初の実行をトリガーします(質問のように、それに適切なルールイベントがあると思います)。

ステップ3-適切なタイミングでルールコンポーネントの再スケジュールを停止する

また、前のステップのルールコンポーネントに「VBOビューから返された結果の数を確認する」ためにルール条件を追加します(同じVBOビューを使用)。結果の実際の数は少なくとも1である必要があります。結果がゼロの場合、このルールコンポーネントのルールアクションは実行されません。これが目的です。また、すべてのユーザーが処理されると、ルールコンポーネントが再スケジュールされないようにします。

ステップ4-cronジョブの頻度を確認する

スケジュールされたルールコンポーネントの各実行は実際にはcronの実行時にのみ発生するため、cronジョブが十分に頻繁に実行されることを確認してください。

結論:このアプローチはルールスケジューラに基づいているため、確かに経過時間は長くなります。しかし、それの利点は、質問の3つのソリューションのいずれと比較しても、「メンバーシップを購入した会社の」の貧しいユーザーは、ルールが完了するのを待つ必要がないことです。サイトで他のことを実行できるようになる前に実行を完了します。

PS:これはD7についてだと思います...

1
Pierre.Vriens

ルールではなく、node_hook_presave()を呼び出すカスタムモジュールを使用することを選択しました。このプロセスでは、会社を保存する前に、会社ノードの「メンバーシップステータス」が変更されたかどうかを確認します。

_/**
 * Implements hook_node_presave().
 */
function lia_company_node_presave($node){
    if($node->type=='company'){
        /**
         * If field_membership_status is set and the value has changed, then execute
         * the function  lia_company_update_membership_for_users($node->nid, $membership_status);
         */
        if(isset($node->original->field_membership_status[LANGUAGE_NONE][0]['tid'])
            && isset($node->field_membership_status[LANGUAGE_NONE][0]['tid'])
            && $node->original->field_membership_status[LANGUAGE_NONE][0]['tid'] <> $node->field_membership_status[LANGUAGE_NONE][0]['tid']) {

            $membership_status = $node->field_membership_status[LANGUAGE_NONE][0]['tid'];
            lia_company_update_membership_for_users($node->nid, $membership_status);
        }
    }
}
_

元の値が設定されていて、新しい値が元の値と等しくなかった場合は、ローカルメソッドlia_company_update_membership_for_users($node->nid, $membership_status)を呼び出します。

そのメソッドは次のようになります。おそらくこれをリファクタリングしてバッチプロセスとして実行する必要があると思います。しかし、他の人が見つけた最適化/フィードバックは大歓迎です。

_/**
 * Implemented by hook_field_presave on Company node when field_membership_status is updated
 * on a Company node so that all of the Users referencing this Company are
 * updated to Member (Corp) or Non-Member, as appropriate.
 *
 * @param $company_nid
 * @param $membership_status_tid
 */
function lia_company_update_membership_for_users($company_nid, $membership_status_tid){
    $query = new EntityFieldQuery();
    $query
        ->entityCondition('entity_type', 'user')
        ->fieldCondition('field_company','target_id',$company_nid,'=');
    $results = $query->execute();

    $member_tid =  1744; // Taxonomy tid for Member
    $non_member_tid = 1749; // Taxonomy tid for Non-Member
    $member_corp_tid = 1745; // Taxonomy tid for Member (Corp)
    $member_status_excluded = array(
        1744, // Active
        1745, // Active (Corp)
        1748, // Deceased
        1746, // Deleted
    );

    foreach($results['user'] as $user){
        $user_wrapper = entity_metadata_wrapper('user',user_load($user->uid));

        /**
         * If the user is not already in an excluded membership_status, then we should update their status.
         * If the Company is being updated to a Member [TID: 1744] then, let's set all eligible Users to a
         * membership_status of Member (Corp) [TID: 1745]. Otherwise, we need to set them to Non-Member [TID: 1749].
         */
        if(isset($user_wrapper->field_membership_status) && !in_array($user_wrapper->field_membership_status->value()->tid, $member_status_excluded)){
            $user_wrapper->field_membership_status = ($membership_status_tid== $member_tid) ? $member_corp_tid : $non_member_tid;
            $user_wrapper->save();
        }
    }

}
_

@ pierre.vriensに回答いただきありがとうございます。時間があれば、おそらくリファクタリングすることになるでしょう。

1
Shaun O