Apex-トリガーデザインパターン

デザインパターンは、コードをより効率的にし、ガバナーの制限に達しないようにするために使用されます。多くの場合、開発者は、オブジェクトのインスタンス化を繰り返す可能性のある非効率的なコードを作成できます。これにより、コードの効率が低下し、パフォーマンスが低下し、ガバナーの制限に違反する可能性があります。これは、一連のレコードに対して動作できるため、トリガーで最も一般的に発生します。

この章では、いくつかの重要なデザインパターン戦略について説明します。

バルクトリガーデザインパターン

実際のビジネスケースでは、一度に数千のレコードを処理する必要がある場合があります。トリガーがそのような状況を処理するように設計されていない場合、レコードの処理中に失敗する可能性があります。トリガーを実装する際に従う必要のあるベストプラクティスがいくつかあります。すべてのトリガーはデフォルトで一括トリガーであり、一度に複数のレコードを処理できます。一度に複数のレコードを処理することを常に計画する必要があります。

多数のレコードを処理する必要があり、以下に示すようにトリガーを記述したビジネスケースについて考えてみます。これは、顧客ステータスが非アクティブからアクティブに変わったときに請求書レコードを挿入するために行ったのと同じ例です。

// Bad Trigger Example
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   
   for (APEX_Customer__c objCustomer: Trigger.new) {
      
      if (objCustomer.APEX_Customer_Status__c == 'Active' && 
         trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         
         // condition to check the old value and new value
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         insert objInvoice;   //DML to insert the Invoice List in SFDC
      }
   }
}

これで、DMLステートメントがループブロックに書き込まれていることがわかります。ループブロックは、少数のレコードのみを処理する場合に機能しますが、数百のレコードを処理する場合は、トランザクションごとのDMLステートメントの制限に達します。 governor limit。ガバナー制限については、次の章で詳しく説明します。

これを回避するには、トリガーを効率的にして、一度に複数のレコードを処理する必要があります。

次の例は、同じことを理解するのに役立ちます-

// Modified Trigger Code-Bulk Trigger
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
   
   for (APEX_Customer__c objCustomer: Trigger.new) {
      
      if (objCustomer.APEX_Customer_Status__c == 'Active' &&
         trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         
         //condition to check the old value and new value
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         InvoiceList.add(objInvoice);//Adding records to List
      }
   }
   
   insert InvoiceList;
   // DML to insert the Invoice List in SFDC, this list contains the all records 
   // which need to be modified and will fire only one DML
}

このトリガーは、リストに対して動作し、リストには変更が必要なすべてのレコードがあるため、1つのDMLステートメントのみを起動します。

このようにして、DMLステートメントガバナーの制限を回避できます。

トリガーヘルパークラス

コード全体をトリガーで記述することも良い習慣ではありません。したがって、以下に示すように、Apexクラスを呼び出し、処理をTriggerからApexクラスに委任する必要があります。Trigger Helperクラスは、トリガーのすべての処理を行うクラスです。

請求書レコードの作成例をもう一度考えてみましょう。

// Below is the Trigger without Helper class
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   List<apex_invoice__c> InvoiceList = new List<apex_invoice__c>();
   
   for (APEX_Customer__c objCustomer: Trigger.new) {
      
      if (objCustomer.APEX_Customer_Status__c == 'Active' &&
         trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
         
         // condition to check the old value and new value
         APEX_Invoice__c objInvoice = new APEX_Invoice__c();
         objInvoice.APEX_Status__c = 'Pending';
         InvoiceList.add(objInvoice);
      }
   }
   
   insert InvoiceList; // DML to insert the Invoice List in SFDC
}

// Below is the trigger with helper class
// Trigger with Helper Class
trigger Customer_After_Insert on APEX_Customer__c (after update) {
   CustomerTriggerHelper.createInvoiceRecords(Trigger.new, trigger.oldMap);
   // Trigger calls the helper class and does not have any code in Trigger
}

ヘルパークラス

public class CustomerTriggerHelper {
   public static void createInvoiceRecords (List<apex_customer__c>
   
   customerList, Map<id, apex_customer__c> oldMapCustomer) {
      List<apex_invoice__c> InvoiceList = new Listvapex_invoice__c>();
      
      for (APEX_Customer__c objCustomer: customerList) {
         
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            oldMapCustomer.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            
            // condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            
            // objInvoice.APEX_Status__c = 'Pending';
            InvoiceList.add(objInvoice);
         }
      }
      
      insert InvoiceList;  // DML to insert the Invoice List in SFDC
   }
}

これでは、すべての処理がヘルパークラスに委任されており、新しい機能が必要な場合は、トリガーを変更せずに、コードをヘルパークラスに追加するだけで済みます。

各sObjectの単一トリガー

各オブジェクトに常に単一のトリガーを作成します。同じオブジェクトに対する複数のトリガーは、ガバナーの制限に達すると、競合やエラーを引き起こす可能性があります。

コンテキスト変数を使用して、要件に応じてヘルパークラスからさまざまなメソッドを呼び出すことができます。前の例を考えてみましょう。createInvoiceメソッドは、レコードが更新されたとき、および複数のイベントでのみ呼び出される必要があるとします。次に、以下のように実行を制御できます。

// Trigger with Context variable for controlling the calling flow
trigger Customer_After_Insert on APEX_Customer__c (after update, after insert) {
   
   if (trigger.isAfter && trigger.isUpdate) {
      // This condition will check for trigger events using isAfter and isUpdate
      // context variable
      CustomerTriggerHelper.createInvoiceRecords(Trigger.new);
      
      // Trigger calls the helper class and does not have any code in Trigger
      // and this will be called only when trigger ids after update
   }
}

// Helper Class
public class CustomerTriggerHelper {
   
   //Method To Create Invoice Records
   public static void createInvoiceRecords (List<apex_customer__c> customerList) {
      
      for (APEX_Customer__c objCustomer: customerList) {
         
         if (objCustomer.APEX_Customer_Status__c == 'Active' &&
            trigger.oldMap.get(objCustomer.id).APEX_Customer_Status__c == 'Inactive') {
            
            // condition to check the old value and new value
            APEX_Invoice__c objInvoice = new APEX_Invoice__c();
            objInvoice.APEX_Status__c = 'Pending';
            InvoiceList.add(objInvoice);
         }
      }
      
      insert InvoiceList; // DML to insert the Invoice List in SFDC
   }
}