5

I came to a situation where I have already 2 triggers implemented(both before update) and I have to add some logic on before update trigger. I am trying to understand the execution flow. how can i understand the execution sequence of trigger. does trigger execution be controlled at any configuration level?

Also I am planning to re-design the two triggers into a single one. but confused with code sequence.

-2

One can not guarantee about the sequence of trigger. Also we can not define the sequence in SFDC.

Further if you have to check the sequence of trigger then you can use system debug to check execution sequence.

  • What exactly mean by use of system debug? – SFDCdev Apr 7 '15 at 12:54
  • I mean you can set debug log for a particular user(lets say for your own user). execute the update operation and see the debug log for that transaction. you can see the sequence of execution in debug log. – Anup Apr 7 '15 at 13:47
  • You can control execution flow. It must be done manually though. – Chris Apr 7 '15 at 19:35
  • 1
  • 1
    Even if you do use the debug log to check when these triggers are executing - that same execution is not guaranteed each time the triggers run. – Chris Apr 13 '15 at 16:54
17

This is something I work to convince my team about and Dan Appleman has a great book out that I definitely recommend that explains trigger structure very well. If controlling the execution of Triggers is needed a software structure needs to be put in place as the .trigger files will not execute in any order. His book has a good basis for a trigger structure that allows you to control the execution of trigger flow very precisely. Here is a basic implementation that we use

public with sharing class TriggerHandler {


public static ITrigger activeFunction = null;
public static map<Id,SObject> sObjectsToUpdate = new map<Id,SObject>();

public static void TriggerHandlerEntry(TriggerState state){


    if(state.TriggerObject == 'Custom_Object_1__c'){
        processCustomObject1TriggerHandler(state);
    }else if(state.TriggerObject == 'Account'){
        processAccountTriggerHandler(state);
    }else if(state.TriggerObject == 'Custom_Object_2__c'){
        processCustomObject2TriggerHandler(state);
    }else if(state.TriggerObject == 'Custom_Object_3__c'){
        processCustomObject3TriggerHandler(state);
    }


    if(sObjectsToUpdate.values().size() > 0){
        update sObjectsToUpdate.values();
    }
}

public static void processCustomObject1TriggerHandler(TriggerState state){

    if(activeFunction != null){
        activeFunction.InProgressEntry(state);
    }
    else{

        activeFunction = new OrderLineItemTriggerHandler();
        activeFunction.MainEntry(state);
    }
}

public static void processAccountTriggerHandler(TriggerState state){

    if(activeFunction != null){
        activeFunction.InProgressEntry(state);
    }
    else{

        activeFunction = new AccountTriggerHandler();
        activeFunction.MainEntry(state);
    }
}

public static void processCustomObject2TriggerHandler(TriggerState state){

    if(activeFunction != null){
        activeFunction.InProgressEntry(state);
    } 
    else{

        activeFunction = new StampOrderTriggerHandler();
        activeFunction.MainEntry(state);
    }
}

public static void processCustomObject3TriggerHandler(TriggerState state){

    if(activeFunction != null){
        activeFunction.InProgressEntry(state);
    } 
    else{

        activeFunction = new BondTriggerHandler();
        activeFunction.MainEntry(state);
    }
}


public interface ITrigger{

    void MainEntry(TriggerState state);

    void InProgressEntry(TriggerState state);
}
}

The author passes around the "TriggerState" information with a multitude of parameters but we added the information into an object called "TriggerState"

public with sharing class TriggerState {

    public String TriggerObject {get; set;}
    public Boolean IsBefore {get; set;}
    public Boolean IsAfter {get; set;}
    public Boolean IsDelete {get; set;}
    public Boolean IsExecuting {get; set;}
    public Boolean IsUpdate {get; set;}
    public Boolean IsInsert {get; set;}
    public list<SObject> NewList {get; set;}
    public list<SObject> OldList {get; set;}
    public map<Id,SObject> NewMap {get; set;}
    public map<Id,SObject> OldMap {get; set;}


    public TriggerState(String triggerObject, Boolean isBefore, Boolean isAfter, Boolean isUpdate, Boolean isDelete,
                    Boolean isInsert, Boolean isExecuting, list<SObject> newList, map<ID,SObject> newMap, list<SObject> oldList, map<ID,SObject> oldMap) {

        this.TriggerObject = triggerObject;
        this.IsBefore = isBefore;
        this.IsAfter = isAfter;
        this.IsUpdate = isUpdate;
        this.IsDelete = isDelete;
        this.IsInsert = isInsert;
        this.IsExecuting = isExecuting;
        this.NewList = newList;
        this.OldList = oldList;
        this.NewMap = newMap;
        this.OldMap = oldMap;
    }
}

Now every trigger class looks like this except that you would pass in the name of the current SObject you are in and create a subsequent handler class:

    trigger Account on Account (before insert, before update) {

    TriggerHandler.TriggerHandlerEntry(new TriggerState('Account', trigger.isBefore, trigger.isAfter, trigger.isUpdate, 
                            trigger.isDelete, trigger.isInsert, trigger.isExecuting, trigger.new, trigger.newMap, trigger.old, trigger.oldMap));
}

Now there are a few different things going on in this structure besides just controlling flow of trigger execution. This is also making a determination between Triggers DML calls and user generated DML statements. Each trigger handler must implement that ITriggerHandler Interface which contains a MainEntry and ActiveEntry method. The TriggerHandler keeps track of an "Active" trigger handler and unless this active function is set all subsequent triggers are routed to that handler's inProgressEntry. If you want the Trigger to execute through its own handlers main entry - simply set the activeFunction static variable to null. When the trigger handler comes in it will be routed to its particular trigger handler. This structure is also creating a centralized list of SObjects to update so that all code can finish executing and then process the update for the objects.

For your situation you may not need all the features this type of structure offers but one thing that is stressed in that book is that if you can justify just a little extra time into putting a good structure in place - it can save you alot of time in the future. Obviously it is still important to keep the scope of the project in mind when implementing something like this but I think that more often than not these systems get larger than anticipated. I have personally run into issues where a lack of a centralized trigger system in a very large application caused me a couple days of headaches.

If you do some research you will find all kinds of trigger structures out there - you may find a different one more suited for your particular case but it is an important question to have none the less.

Here is the link for the book I was mentioning.

  • 1
    +1 for this book, it completely changed the way I thought about design in Apex, and is quite readable along the way! – Brian Mansfield Apr 7 '15 at 13:53
  • 1
    Great answer and got to agree with you and Bri. Dan Appleman's book is a must read for any serious APEX developer! Thanks! – Richard N Apr 24 '15 at 20:35
0

You can leverage any of the trigger (since they both run on same events) and include your logic as required. Just don't forget to use appropriate conditions while adding your logic. This will make sure the code unit is executed when the desired action is performed. Also, unfortunately, you cannot control which trigger will execute first if they are on same events.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.