Apex-trigger-design-pattern
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クラスを呼び出して、トリガーからApexクラスに処理を委任する必要があります。 トリガーヘルパークラスは、トリガーのすべての処理を行うクラスです。
請求書レコードの作成例をもう一度考えてみましょう。
//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
}
}