One of the most common questions on this forum is around trigger recursion. There are a few standard ways to prevent recursion. My method of choice when it comes to checking for field changes, is Dan Appleman's 'Correct' old Value method found in Chapter 6 of his book Advanced Apex Programming. I have used it many times and like the concept.
My question revolves around how to use it correctly and most efficiently in an org with multiple complex pieces of separate logic within a single trigger. Let's say I have the simple Trigger Framework shown below
Trigger
trigger OpportunityTrigger on Opportunity (after delete, after insert, after undelete, after update, before delete, before insert, before update) {
//Other Scenarios (Before insert, before update, etc.)
if(Trigger.isAfter && Trigger.isUpdate){
OpportunityTriggerHandler.OppAfterUpdate(trigger.new, trigger.old, trigger.newMap, trigger.oldMap);
}
}
Let's say this is my simplified handler class.
public class OpportunityTriggerHandler {
public void OppAfterUpdate(list<Opportunity> newOpps, list<Opportunity> oldOpps, map<Id,Opportunity> newMap, map<Id,Opportunity> oldMap){
myMethod1(newOpps, oldOpps, newMap, oldMap);
}
private void myMethod1(List<Opportunity> newOpps, List<Opportunity> oldOpps, map<Id, Opportunity> newMap, map<Id, Opportunity> oldMap){
//MY LOGIC HERE
}
}
If I wanted to check for opps that have just changed to 'Won' using Dan Appleman's method, I would add a static map to the handler class and add this logic to myMethod1
public class OpportunityTriggerHandler {
Private static Map <Id,boolean> oldIsWonMap = null;
//................
private void myMethod1(List<Opportunity> newOpps, List<Opportunity> oldOpps, map<Id, Opportunity> newMap, map<Id, Opportunity> oldMap){
if(oldIsWonMap == null) {
oldIsWonMap = new map<Id,boolean >();
}
set<Id> oppIds = new set<Id>();
for(Opportunity o : newOpps){
boolean oldIsWon = (oldIsWonMap.containsKey(o.id)) ? oldIsWonMap.get(o.id) : oldmap.get(o.id).isWon;
if(o.isWon && !oldIsWon){
oppIds.add(o.Id);
}
if(oldIsWon != o.isWon) {
oldIsWonMap.put(o.id,o.isWon);
}
}
//MY LOGIC HERE USING THE OPP IDS OF JUST WON OPPS
}
}
This works great and now I have a map that holds the 'correct' old values and even if a workflow causes this trigger to fire a 2nd time, the logic will not repeat as I am checking against the correct old values from the map.
My question
How do I use this method if I have multiple, separate methods that need to check for a field change. Suppose I have 2 (or more) methods in my handler class that need to perform logic based on only opps that were just won. How would I best implement this paradigm
public class OpportunityTriggerHandler {
public void OppBeforeUpdate(list<Opportunity> newOpps){
myMethod1(newOpps, oldOpps, newMap, oldMap);
}
public void OppAfterUpdate(list<Opportunity> newOpps, list<Opportunity> oldOpps, map<Id,Opportunity> newMap, map<Id,Opportunity> oldMap){
myMethod2(newOpps, oldOpps, newMap, oldMap);
myMethod3(newOpps, oldOpps, newMap, oldMap);
}
private void myMethod1(List<Opportunity> newOpps, List<Opportunity> oldOpps, map<Id, Opportunity> newMap, map<Id, Opportunity> oldMap){
//MY LOGIC HERE
}
private void myMethod2(List<Opportunity> newOpps, List<Opportunity> oldOpps, map<Id, Opportunity> newMap, map<Id, Opportunity> oldMap){
//MY LOGIC HERE
}
private void myMethod3(List<Opportunity> newOpps, List<Opportunity> oldOpps, map<Id, Opportunity> newMap, map<Id, Opportunity> oldMap){
//MY LOGIC HERE
}
}