1

Early on as an Apex developer, I adopted the following one-trigger-per-object design pattern:

Class

public with sharing class caseTriggerHandler {

    public static boolean firstRun = true;
    private boolean m_isExecuting = false;

    public caseTriggerHandler (boolean isExecuting){
        m_isExecuting = isExecuting;
    }

    public void OnBeforeInsert(List<Case> newCases){
        // EXECUTE BEFORE INSERT LOGIC 
    }

    public void OnAfterInsert(Case[] newCases, Map<Id, Case> caseNewMap){
        // AFTER INSERT LOGIC
    }

    public void OnBeforeUpdate(Case[] oldCases, Case[] updatedCases, Map<Id, Case> caseOldMap, Map<Id, Case> caseNewMap){
        // BEFORE UPDATE LOGIC
    }

    public void OnAfterUpdate(Case[] oldCases, Case[] updatedCases, Map<Id, Case> caseNewMap, Map<Id, Case> caseOldMap){
        // PREVENT RECURSIVE TRIGGER
        if (firstRun){
            firstRun = false;
            system.debug('First Run');
        } else {
            System.debug('Already ran!');
            return;
        }
        // AFTER UPDATE LOGIC    
    }

    public void OnBeforeDelete(Case[] CasesToDelete, Map<Id, Case> caseMap){
        // BEFORE DELETE LOGIC
    }

    public void OnAfterDelete(Case[] deletedCases, Map<Id, Case> caseMap){
        // AFTER DELETE LOGIC
    }

    public void OnUndelete(Case[] restoredCases){
        // AFTER UNDELETE LOGIC
    }

    public boolean IsTriggerContext{
        get{ return m_isExecuting;}
    }
}

Trigger

trigger caseTrigger on Case (after delete, after insert, after undelete, 
after update, before delete, before insert, before update) {
    caseTriggerHandler handler = new caseTriggerHandler (true);

    /* Before Insert */
    if(Trigger.isInsert && Trigger.isBefore){
        handler.OnBeforeInsert(Trigger.new); 
    }
    /* After Insert */
    else if(Trigger.isInsert && Trigger.isAfter){
        handler.OnAfterInsert(Trigger.new, Trigger.newMap);
    }
    /* Before Update */
    else if(Trigger.isUpdate && Trigger.isBefore){
        handler.OnBeforeUpdate(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap);
    }
    /* After Update */
    else if(Trigger.isUpdate && Trigger.isAfter){
        handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.newMap, Trigger.oldMap);
    } 
    /* Before Delete */
    else if(Trigger.isDelete && Trigger.isBefore){
        handler.OnBeforeDelete(Trigger.old, Trigger.oldMap);
    }
    /* After Delete */
    else if(Trigger.isDelete && Trigger.isAfter){
        handler.OnAfterDelete(Trigger.old, Trigger.oldMap);
    }
    /* After Undelete */
    else if(Trigger.isUnDelete){
        handler.OnUndelete(Trigger.new); 
    }
}

However, I now cannot recall how exactly the parameters in the trigger's methods feed the class' methods.

For instance, the trigger's handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.newMap, Trigger.oldMap); obviously calls and populates:

OnAfterUpdate(Case[] oldCases, Case[] updatedCases, Map<Id, Case> caseNewMap, Map<Id, Case> caseOldMap) in the class.

But the list and map parameters in the class method are generic (apart from the arbitrary words used for their variables). So how is it that oldCases ends up specifically storing trigger.old, updatedCases storing trigger.new and so on?

I bet the answer is obvious, staring me in the face, right under my nose, etc...

1
  • 1
    N.B. the recursion control will fail whenever the transaction contains more than 200 records (such as in a BULK API use case) as records 201+ will be ignored in the after update method
    – cropredy
    Jul 23, 2018 at 0:03

1 Answer 1

1

The parameters are populated by position; the first position's value from the trigger's method call is set as the value for the method's first position's value in the method signature, the second to the second, and so on.

There's no particular mechanism that guarantees that oldCases is mapped to Trigger.old in the event that a developer accidentally swaps the parameter values.

// Correct
public void OnBeforeUpdate(Case[] oldCases, Case[] newCases) { ...
...
handler.OnBeforeUpdate(Trigger.old, Trigger.new);

// Incorrect
public void OnBeforeUpdate(Case[] oldCases, Case[] newCases) { ...
...
handler.OnBeforeUpdate(Trigger.new, Trigger.old);

For this reason, it's important to have a consistent API, because if you screw up the order of the parameters, the compiler has no idea of knowing you screwed up (assuming the data types are still correct).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .