Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The purpose of this trigger framework is to follow the same implementation across all triggers that may be implemented in University of Sydney’s Salesforce instance.

Base Components

The base components of the trigger frame framework includes:

  • USYD_TriggerHandlerBase - contains the core methods of controlling the trigger.

Expand
Code Block
languagejava
public abstract class USYD

...

  • After_Delete__c - checkbox to control whether the after delete event will be executed.

  • After_Insert__c - checkbox to control whether the after insert event will be executed.

  • After_Undelete__c - checkbox to control whether the after insert event will be executed.

  • After_Update__c - checkbox to control whether the after insert event will be executed.

  • Before_Delete__c - checkbox to control whether the after insert event will be executed.

  • Before_Insert__c - checkbox to control whether the after insert event will be executed.

  • Before_Update__c - checkbox to control whether the after insert event will be executed.

_TriggerHandlerBase {
    private final string ALL_TRIGGER_KEY = 'USYD_ALL_TRIGGERS';   
    private final TriggerOperation TEST_DEFAULT_OP = TriggerOperation.BEFORE_INSERT;   
    private String triggerName = '-Untitled-';
    @testvisible
    private USYD_Trigger_Setting__mdt allTriggerConfig = USYD_Trigger_Setting__mdt.getInstance(ALL_TRIGGER_KEY);
    @testvisible
    private USYD_Trigger_Setting__mdt triggerConfig = null; 
    USYD_System_Config__c

...

 config = USYD_System_Config__c

...

The Base Class

The trigger handler base class contains the core methods to

Expand
titleUSYD_TriggerHandlerBase.cls
Code Block
/*******************************************************************************************************
 * @description       : Abstract class for trigger handlers
 * @author            : Aldrin Rasdas
 * @date			  : 31-MAY-2023
 * -----------------------------------------------------------------------------------------------------
 * Developer            Date.getInstance();    
    
    public virtual void beforeInsert(List<SObject> newRecords){}
    public virtual void beforeUpdate(List<SObject> oldRecords, List<SObject> newRecords, Map<ID, SObject> oldRecordMap, Map<ID, SObject> newRecordMap){}
    public virtual void beforeDelete(List<SObject> oldRecords, Map<ID, SObject> oldRecordMap){}
    public virtual void afterInsert(List<SObject> newRecords, Map<ID, SObject> newRecordMap){}
    public virtual void afterUpdate(List<SObject> oldRecords, List<SObject> newRecords, Map<ID, SObject> oldRecordMap, Map<ID, SObject> newRecordMap){}
    public virtual void afterDelete(List<SObject> oldRecords, Map<ID, SObject> oldRecordMap){}
    public virtual void afterUndelete(List<SObject> newRecords, Map<ID, SObject> newRecordMap){}
    
    public USYD_TriggerHandlerBase(String triggerName) {
        this.triggerName = triggerName;
        this.triggerConfig = USYD_Trigger_Setting__mdt.getInstance(triggerName);
    }
    
    private void Log(String  Mod ID      Description
 * -----------------------------------------------------------------------------------------------------
 * Aldrin Rasdas		31-MAY-2023		1000source, String message) {
		if (config!=null && config.Trigger_Handler_Logging__c !=null && config.Trigger_Handler_Logging__c==true) {
            SM_ApplicationLog.addLog(SM_ApplicationLog.LOGGING_LEVEL.INFO.name(), 
                                     Initial ver. introduced in TAPSS-1205
 ******************************************************************************************************/
public abstract class USYD_TriggerHandlerBase {
    private final string ALL_TRIGGER_KEY = 'USYD_ALL_TRIGGERS';   
    private final TriggerOperation TEST_DEFAULT_OP = TriggerOperation.BEFORE_INSERTmessage, source, null);                
        }
    }
    
    public void execute() {
        integer triggerSize = trigger.size;
        if (!trigger.isExecuting && !Test.isRunningTest()) return;
        if (!(triggerSize>0) && !Test.isRunningTest()) return;
       private String
triggerName = '-Untitled-';     @testvisible private USYD_Trigger_Setting__mdt allTriggerConfig = USYD_Trigger_Setting__mdt.getInstance(ALL_TRIGGER_KEY);if (this.isTriggerEnabled()) {
                this.Log('USYD_TriggerHandlerBase.execute()',
       @testvisible private USYD_Trigger_Setting__mdt triggerConfig = null;      USYD_System_Config__c config = USYD_System_Config__c.getInstance();    'Trigger ' + this.triggerName + ' executed for event ' public virtual void beforeInsert(List<SObject> newRecords){}+ String.valueOf(Trigger.operationType) + 
     public virtual void beforeUpdate(List<SObject> oldRecords, List<SObject> newRecords, Map<ID, SObject> oldRecordMap, Map<ID, SObject> newRecordMap){}     public virtual void beforeDelete(List<SObject> oldRecords, Map<ID, SObject> oldRecordMap){}
    public virtual void afterInsert(List<SObject> newRecords, Map<ID, SObject> newRecordMap){}' with ' + String.valueOf(triggerSize) + ' records: trigger switch is ON.');           public virtual void afterUpdate(List<SObject> oldRecords,
List<SObject> newRecords, Map<ID, SObject> oldRecordMap, Map<ID, SObject> newRecordMap){}     public virtual void afterDelete(List<SObject> oldRecords, Map<ID, SObject> oldRecordMap){}
    public virtual void afterUndelete(List<SObject> newRecords, Map<ID, SObject> newRecordMap){}this.run(Trigger.operationType);
        } else {
                this.log('USYD_TriggerHandlerBase.execute()',
  //STUBS for override in the extended class     /*     //required constructor     public USYD_TriggerHandlerName(String triggerName) {
    	super(triggerName);
 'Trigger ' + this.triggerName + ' NOT executed for event ' + String.valueOf(Trigger.operationType) + 
  } 	//implement as required     public override void beforeInsert(List<SObject> newRecords){}     public override void beforeUpdate(List<SObject> oldRecords, List<SObject> newRecords, Map<ID, SObject> oldRecordMap, Map<ID, SObject> newRecordMap){}    ' with ' + String.valueOf(triggerSize) + ' records: trigger switch is OFF.');     public override void beforeDelete(List<SObject> oldRecords, Map<ID, SObject> oldRecordMap){}
    public override void afterInsert(List<SObject> newRecords, Map<ID,}
SObject> newRecordMap){}     public override void afterUpdate(List<SObject> oldRecords, List<SObject> newRecords, Map<ID, SObject> oldRecordMap, Map<ID, SObject> newRecordMap){ SM_ApplicationLog.publishLogs(); 
    }

    public overrideboolean void afterDeleteisTriggerEnabled(List<SObject>) oldRecords,{
Map<ID, SObject> oldRecordMap){}     public overrideboolean voidenabled afterUndelete(List<SObject> newRecords, Map<ID, SObject> newRecordMap){}
	*/= false;
        String operation = String.valueOf(trigger.operationType);
 //SAMPLE trigger code     /*
    trigger AccountTrigger on Account (before insert, before update) {if (operation==null && Test.isRunningTest()) operation = string.valueOf(TEST_DEFAULT_OP);
        
        Stringenabled triggerName= allTriggerConfig!= Stringnull && (boolean.valueOf(this)allTriggerConfig.splitget(':')[0]operation + '__c'))==true);
        USYD_TriggerHandlerNameenabled handler = new USYD_TriggerHandlerName(triggerName);
        handler.execute()enabled ? triggerConfig!=null && (boolean.valueOf(triggerConfig.get(operation + '__c'))==true) : false;

   }  	*/   return enabled;
    } public USYD_TriggerHandlerBase(String triggerName) {   
    
this.triggerName = triggerName;  private void run(System.TriggerOperation op) {
  this.triggerConfig = USYD_Trigger_Setting__mdt.getInstance(triggerName)     if (op==null && Test.isRunningTest()) op = TEST_DEFAULT_OP;
    }    switch on op {
   private void Log(String source, String message) { 		if (config!=null && config.Trigger_Handler_Logging__c !=null && config.Trigger_Handler_Logging__c==true) { when BEFORE_INSERT {
                SM_ApplicationLogthis.addLogbeforeInsert(SM_ApplicationLog.LOGGING_LEVEL.INFO.name(),trigger.new);
            }
            when BEFORE_UPDATE {
                 message, source, nullthis.beforeUpdate(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap);
            }
           } when BEFORE_DELETE {
 }          public void execute() {
   this.beforeDelete(Trigger.old, Trigger.oldMap);
       integer triggerSize = trigger.size;  }
      if (!trigger.isExecuting && !Test.isRunningTest()) return;  when AFTER_INSERT {
    if (!(triggerSize>0) && !Test.isRunningTest()) return;
                 if (this.isTriggerEnabled()) {afterInsert(Trigger.new, Trigger.newMap);
            }
    this.Log('USYD_TriggerHandlerBase.execute()',        when AFTER_UPDATE {
               'Trigger ' + this.triggerName + ' executed for event ' + String.valueOf(Trigger.operationType) + this.afterUpdate(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap);
            }
            when AFTER_DELETE {
      ' with ' + String.valueOf(triggerSize) + ' records: trigger switch is ON.'); this.afterDelete(Trigger.old, Trigger.oldMap);
            }
            when AFTER_UNDELETE {
                this.runafterUndelete(Trigger.operationTypenew, Trigger.newMap);
            } else {          
      this.log('USYD_TriggerHandlerBase.execute()',  }
                       'Trigger ' + this.triggerName + ' NOT executed for event ' + String.valueOf(Trigger.operationType) + 
                         ' with ' + String.valueOf(triggerSize) + ' records: trigger switch is OFF.');            
        }
        SM_ApplicationLog.publishLogs(); 
    }

    public boolean isTriggerEnabled() {
        boolean enabled = false;
        String operation = String.valueOf(trigger.operationType);
        if (operation==null && Test.isRunningTest()) operation = string.valueOf(TEST_DEFAULT_OP);
        
        enabled = allTriggerConfig!=null && (boolean.valueOf(allTriggerConfig.get(operation + '__c'))==true);
        enabled = enabled ? triggerConfig!=null && (boolean.valueOf(triggerConfig.get(operation + '__c'))==true) : false;

        return enabled;
    }       
    
    @testvisible
    private void run(System.TriggerOperation op) {
        if (op==null && Test.isRunningTest()) op = TEST_DEFAULT_OP;
        switch on op {
            when BEFORE_INSERT {
                this.beforeInsert(trigger.new);
}
}
  • USYD_Trigger_Setting__mdt custom metadata type and its fields, namely:

    • After_Delete__c- checkbox to control whether the after delete event will be executed.

    • After_Insert__c - checkbox to control whether the after insert event will be executed.

    • After_Undelete__c- checkbox to control whether the after insert event will be executed.

    • After_Update__c- checkbox to control whether the after insert event will be executed.

    • Before_Delete__c - checkbox to control whether the after insert event will be executed.

    • Before_Insert__c- checkbox to control whether the after insert event will be executed.

    • Before_Update__c- checkbox to control whether the after insert event will be executed.

  • USYD_System_Config__c.Trigger_Handler_Logging__c field - checkbox field whether to control whether to log trigger handler invocation. SM_ApplicationLog class is used for logging.

How to Implement

To use the framework, create an apex trigger as you would normally do. Kindly note, that the developer still needs to define the event that needs to be processed in the trigger itself. This includes the before and after events of specific trigger operation (insert, update, delete) that the programmer intend to catch as per business requirement.

  1. Create the handler class by extending the USYD_TriggerHandlerBase class.

  2. Create include a constructor method which carries a String parameter to pass/contain the trigger name (which can be automatically determined from the trigger body).

  3. Override the applicable events (methods) that need to be implemented/executed (i.e. beforeInsert, beforeUpdate, etc.).

  4. Write the business logic. It is recommended to have the business logic written in a separate method and invoke it from the overridden event/method, rather than in the event itself. This promotes separation of event invocation from business logic. The developer may also opt to have business logic written in a separate class and just invoke it from the applicable events.
    Sample Trigger Handler Class:

    Code Block
    languagejava
    //AccountTriggerHandler.cls
    public class AccountTriggerHandler extends USYD_TriggerHandlerBase{
        //constructor as explained in step #2
        public TriggerTestTriggerHandler(String triggerName) {
            super(triggerName);
      

...

  1.   }
        
        //overridden 

...

  1. event 

...

  1. as 

...

  1. described in step #3
    	public override void beforeInsert(List<sObject> newRecords){
            

...

  1. for (Account record : (Account[])newRecords) {
                

...

  1. //invoke before insert business logic as described in step #4
       

...

  1.  

...

  1.  

...

  1.    }
        }
        
       

...

  1.  //overridden event as described in step #3
    	public override void beforeUpdate(List<sObject> oldRecords, List<sObject> newRecords, Map<ID, sObject> oldRecordMap, Map<ID, sObject> newRecordMap){
          

...

  1.  

...

  1.  

...

  1. for (Account record 

...

  1. : (Account[])newRecords) {
               

...

  1.  //invoke before update business logic as described in step #4
     

...

  1.  

...

  1.       }
        

...

  1. }
    }
  2. Create the Apex trigger.

  3. In the trigger code, determine the name of the current trigger by having the this code just before invoking the trigger handler class:
    String triggerName = String.valueOf(this).split(':')[0];

  4. Below the line that determines the trigger name, define and instantiate an instance of the trigger that you have written.

  5. Execute the logic by invoking the trigger handler instance execute() method.
    Sample Trigger:

    Code Block
    languagejava
    //AccountTrigger.apxt
    trigger AccountTrigger on Account(before insert, before update) {
        String triggerName = String.valueOf(this).split(':')[0]; //step #6
    	AccountTriggerHandler handler = new AccountTriggerHandler(triggerName); //step #7
        handler.execute(); //step #8
    }
  6. Create a new USYD_Trigger_Setting__mdt custom metadata record with Label/USYD Trigger Setting Name as the name of the Apex trigger.

  7. Tick (mark as checked) the trigger events that need to be executed/enabled.

    USYD Trigger Setting record. This should be part of the package or branch for deploymentImage Added