function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Ashritha ReddyAshritha Reddy 

Context Handler Methods

what is Context-Specific Handler Methods in triggers?
 can any one explain wid example please>?
 
salesforce mesalesforce me
Sunil02Kumar
Hi Zach,

It is always a best practice to write a single trigger on object and process all operation in apex class (we called it as handler) instead of 
writing logic in trigger. In handler you can specify the order of event based on different context variables. If you write many triggers in on object, 
then you cannot control the order in which triggers gets executed.

For best practice, use below suggestion:

1. Pass all trigger context variables to static method (say "OperationManager")in handler class.
2. Create different static methods for different operations which you need to perform.
3. Control the order of different operation in "OperationManager" static method based on context variables.
4. Also you can create different apex classes and call them from "OperationManager" static method.
trigger JobTrigger on Job__c (after delete, after insert, after undelete, after update, before delete, before insert, before update)
{
    String errMsg = JobTriggerHandler.OperationManager(trigger.new, trigger.old, trigger.newMap,trigger.oldMap,
                trigger.isBefore,trigger.isAfter,trigger.isInsert,trigger.isUpdate,trigger.isDelete,   trigger.isUndelete,trigger.isExecuting);

    // If error occured, then display it to user
    if (errMsg != null) {
        if (trigger.isInsert || trigger.isUpdate || trigger.isUndelete) { trigger.new[0].addError(errMsg); } else { trigger.old[0].addError(errMsg); }
    }
}
public with sharing class JobTriggerHandler {
     // Main entry point for this handler.
    public static String OperationManager(list<Job__c> newList, list<Job__c> oldList, map<Id, Job__c> newMap,map<Id, Job__c> oldMap,
        boolean isBefore,boolean isAfter, boolean isInsert, boolean isUpdate,boolean isDelete,boolean isUndelete, boolean isExecuting) {
        String errMsg;
        try {
            // Only run a method if previous methods didn't fail
            //As you want to perform operation on before insert and before update
            if (isBefore && (isUpdate || isInsert)  && errMsg == null) {
                // Find hourly rate by calling static method defined below
                errMsg = computeHourlyRate(newList);
            }
            //here you can call any other static methods or some other class method to perform desired operations
            //keep on adding the other logic which needs to be processed through trigger
            //through context variables you can control their execution
        } catch (Exception ex) { 
            errMsg = ex.getMessage() + ' at ' + ex.getLineNumber().format();
        }
        return errMsg;
    }
    
    //Below method will compute hourly rate
    public static String computeHourlyRate(list<Job__c> newList) {
        String errMsg;
        try {
            Set<Id> AccID = new Set<Id>();
            Set<String> JobType = new Set<String>();
            Map<String,Rate_card__c> RateCardMap = new Map<String,Rate_card__c>();
            for (Job__c Job: newList){
                AccID.add(Job.Account__c); 
                JobType.add(Job.Type__c);
            }
            For(Rate_Card__c RateCard : [SELECT Id, Hourly_Rate__c, Account__c, Type__c FROM Rate_card__c  WHERE Account__c IN: AccID]){
                RateCardMap.put(RateCard.Account__c + RateCard.Type__c, RateCard);
            }
            For (Job__c Jobs: newList){
                if(RateCardMap.containsKey(Jobs.Account__c+Jobs.Type__c)){ 
                    Jobs.Hourly_Rate__c = RateCardMap.get(Jobs.Account__c + Jobs.Type__c).Hourly_Rate__c;
                }
            }
        } catch (Exception ex) { errMsg = ex.getMessage(); }
        return errMsg;
    }
}


 
Amit Chaudhary 8Amit Chaudhary 8
Please check below post for Trigger best partice and Trigger frame work
1) http://amitsalesforce.blogspot.in/2015/06/trigger-best-practices-sample-trigger.html
2) https://developer.salesforce.com/page/Apex_Code_Best_Practices

1) One Trigger Per Object
A single Apex Trigger is all you need for one particular object. If you develop multiple Triggers for a single object, you have no way of controlling the order of execution if those Triggers can run in the same contexts

2) Logic-less Triggers
If you write methods in your Triggers, those can’t be exposed for test purposes. You also can’t expose logic to be re-used anywhere else in your org.

3) Context-Specific Handler Methods
Create context-specific handler methods in Trigger handlers

Please check below example of Context-Specific Handler . In that we can add all logic inside the action class and method handling in handler class.

Example 1:-
Create one Trigger "AccountTrigger"
trigger AccountTrigger on Account( after insert, after update, before insert, before update)
{

    AccountTriggerHandler handler = new AccountTriggerHandler(Trigger.isExecuting, Trigger.size);
    
    if( Trigger.isInsert )
    {
        if(Trigger.isBefore)
        {
            handler.OnBeforeInsert(trigger.New);
        }
        else
        {
            handler.OnAfterInsert(trigger.New);
        }
    }
    else if ( Trigger.isUpdate )
    {
        if(Trigger.isBefore)
        {
            handler.OnBeforeUpdate(trigger.New ,trigger.Old,Trigger.NewMap,Trigger.OldMap);
        }
        else
        {
            handler.OnAfterUpdate(trigger.New ,trigger.Old,Trigger.NewMap,Trigger.OldMap);
        }
    }
}
Create one Trigger Handler Class
 
public with sharing class AccountTriggerHandler 
{
    private boolean m_isExecuting = false;
    private integer BatchSize = 0;
    public static boolean IsFromBachJob ;
    public static boolean isFromUploadAPI=false;
    
    public AccountTriggerHandler(boolean isExecuting, integer size)
    {
        m_isExecuting = isExecuting;
        BatchSize = size;
    }
            

    public void OnBeforeInsert(List<Account> newAccount)
    {
        system.debug('Account Trigger On Before Insert');
    }
    public void OnAfterInsert(List<Account> newAccount)
    {
        system.debug('Account Trigger On After Insert');
    }
    public void OnAfterUpdate( List<Account> newAccount, List<Account> oldAccount, Map<ID, Account> newAccountMap , Map<ID, Account> oldAccountMap )
    {
        system.debug('Account Trigger On After Update ');
        AccountActions.updateContact (newAccount);
    }
    public void OnBeforeUpdate( List<Account> newAccount, List<Account> oldAccount, Map<ID, Account> newAccountMap , Map<ID, Account> oldAccountMap )
    {
        system.debug('Account Trigger On Before Update ');
    }

    @future 
    public static void OnAfterUpdateAsync(Set<ID> newAccountIDs)
    {

    }      
    public boolean IsTriggerContext
    {
        get{ return m_isExecuting;}
    }
    
    public boolean IsVisualforcePageContext
    {
        get{ return !IsTriggerContext;}
    }
    
    public boolean IsWebServiceContext
    {
        get{ return !IsTriggerContext;}
    }
    
    public boolean IsExecuteAnonymousContext
    {
        get{ return !IsTriggerContext;}
    }
}
Create one Trigger Action Class
public without sharing class AccountActions 
{
    public static void updateContact ( List<Account> newAccount)
    {
        // Add your logic here
    }
}


Context-Specific Handler Methods

One best-practice that I have picked up is to create context-specific handler methods in my Trigger handlers. In the above example, you’ll see that I’ve created a specific handler method just for after insert. If I were to implement new logic that ran on after update, I’d simply add a new handler method for it. Again, this handler method would be in the handler class, and not the Trigger. In this case, I might add some very light routing logic into the Trigger itself just so that the correct handler method is invoked:
trigger OpportunityTrigger on Opportunity (after insert, after update) {

  if(Trigger.isAfter && Trigger.isInsert) {
    OpportunityTriggerHandler.handleAfterInsert(Trigger.new);
  } else if(Trigger.isAfter && Trigger.isUpdate) {
    OpportunityTriggerHandler.handleAfterInsert(Trigger.new, Trigger.old);
  }

}
https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices



Let us know if this will help you


 
rornelasrornelas
Amit,

Great example thanks for posting! I get the AccountTrigger (logic less) calls the Handler class. Two thins that are unclear:
1. The trigger Action Class
2. How to work with handler methods that work with Map and Set collection types (e.g. before update)

This is what I got so far. I'm reviewing your article http://amitsalesforce.blogspot.in/2015/06/trigger-best-practices-sample-trigger.html
to see if I can maybe get one of your methods into my code to test.

Logic less Trigger
trigger contact_trigger on Contact (after update) {
	contact_trigger_handler ath = new contact_trigger_handler();
	
    
    /* Before Insert 
    if(Trigger.isInsert && Trigger.isBefore){
       handler.OnBeforeInsert(Trigger.new);*/
    
    /* After Insert 
    else if(Trigger.isInsert && Trigger.isAfter){
        handler.OnAfterInsert(Trigger.new);*/
    
     /* Before Update 
    else if(Trigger.isUpdate && Trigger.isBefore){
        handler.OnBeforeUpdate(Trigger.old, Trigger.new, Trigger.newMap);*/

    //After Update
	if(trigger.isUpdate && trigger.isAfter){
		if(contact_trigger_handler.isFirstTime){
			contact_trigger_handler.isFirstTime = false;
			ath.onAfterUpdateEvent(trigger.new);
		}
	}
    
    /* Before Delete 
    else if(Trigger.isDelete && Trigger.isBefore){
        handler.OnBeforeDelete(Trigger.old, Trigger.oldMap);
    }*/

    /* After Undelete 
    else if(Trigger.isUnDelete){
        handler.OnUndelete(Trigger.new);
    }*/

Handler Class
public class contact_trigger_handler {
	public static Boolean isFirstTime = True;
    
    public Contact_trigger_handler() { }
        
        public void onAfterUpdateEvent(List<Contact> con_List) {
            List<Contact> con_to_be_update = new List<Contact>();
            system.debug('Trigger new Size ' +con_list.size());
            for(Contact con :con_List) {
			system.debug('Trigger Updating Properly .... ' + con.Languages__c);            
            con_to_be_update.add(new Contact(Id=con.Id, Second_Language__c = con.Languages__c));
            }
            if(con_to_be_update.size() > 0){
                update con_to_be_update;
            }
        }
}