Apex - Patrones de diseño de disparadores
Los patrones de diseño se utilizan para hacer que nuestro código sea más eficiente y evitar alcanzar los límites del regulador. A menudo, los desarrolladores pueden escribir código ineficiente que puede provocar la creación de instancias repetidas de objetos. Esto puede resultar en un código ineficiente, de bajo rendimiento y potencialmente en el incumplimiento de los límites del regulador. Esto ocurre con mayor frecuencia en los desencadenantes, ya que pueden operar contra un conjunto de registros.
Veremos algunas estrategias de patrones de diseño importantes en este capítulo.
Patrones de diseño de disparadores a granel
En un caso comercial real, es posible que necesite procesar miles de registros de una sola vez. Si su disparador no está diseñado para manejar tales situaciones, entonces puede fallar al procesar los registros. Existen algunas prácticas recomendadas que debe seguir al implementar los activadores. Todos los disparadores son disparadores masivos de forma predeterminada y pueden procesar varios registros a la vez. Siempre debe planificar procesar más de un registro a la vez.
Considere un caso comercial, en el que necesita procesar una gran cantidad de registros y ha escrito el activador como se indica a continuación. Este es el mismo ejemplo que habíamos tomado para insertar el registro de factura cuando el estado del cliente cambia de inactivo a activo.
// 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
}
}
}
Ahora puede ver que la Declaración DML se ha escrito para el bloque de bucle que funcionará al procesar solo unos pocos registros, pero cuando esté procesando algunos cientos de registros, alcanzará el límite de Declaración DML por transacción, que es el governor limit. Veremos en detalle los límites del gobernador en un capítulo posterior.
Para evitar esto, tenemos que hacer que el disparador sea eficiente para procesar varios registros a la vez.
El siguiente ejemplo le ayudará a entender lo mismo:
// 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
}
Este disparador solo disparará 1 declaración DML ya que estará operando sobre una Lista y la Lista tiene todos los registros que necesitan ser modificados.
De esta manera, puede evitar los límites del regulador de la declaración DML.
Activador de clase auxiliar
Escribir todo el código en el disparador tampoco es una buena práctica. Por lo tanto, debe llamar a la clase Apex y delegar el procesamiento de Trigger a la clase Apex como se muestra a continuación. La clase Trigger Helper es la clase que realiza todo el procesamiento del disparador.
Consideremos nuevamente nuestro ejemplo de creación de registros de facturas.
// 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
}
Clase de ayudante
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
}
}
En esto, todo el procesamiento se ha delegado a la clase de ayuda y cuando necesitamos una nueva funcionalidad simplemente podemos agregar el código a la clase de ayuda sin modificar el disparador.
Disparador único en cada sObject
Cree siempre un único disparador en cada objeto. Varios disparadores en el mismo objeto pueden causar conflictos y errores si alcanza los límites del regulador.
Puede usar la variable de contexto para llamar a los diferentes métodos de la clase auxiliar según el requisito. Considere nuestro ejemplo anterior. Supongamos que se debe llamar a nuestro método createInvoice solo cuando el registro se actualiza y en varios eventos. Entonces podemos controlar la ejecución como se muestra a continuación:
// 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
}
}