Implement a Salesforce Trigger Framework

Terry’s Tidbits: Salesforce 2019 Spring Release Top Ten
February 1, 2019
Terry’s Tidbits: Single Sign On with Microsoft Azure AD
April 3, 2019

Implement a Salesforce Trigger Framework

Apex Trigger Framework
Applies to: Lightning & Classic

When creating an apex trigger in Salesforce, there are many best practices. Amit Chaudhary has a great blog covering many of them. You can read his post by clicking here. He also talks about the importance of having a good framework. I’ve used a few variations and each of them have their pros and cons. I wanted to share with you the one I currently use and why I tend like it.

The creator of this trigger framework is Kevin Ohara. You can download it from his GitHub account by clicking here. So why do I like this framework over some of the others. Mostly, I like the minimalist approach Kevin uses. Now, please don’t confuse me with the brilliant developers who build these things. I am not that smart. But, I certainly can appreciate good code when I see it.

Let’s walk through how to implement this trigger framework.

Step 1

First, you’ll want to go to Kevin’s GitHub page.

Scroll down to the Overview section and press the “Deploy to Salesforce Org” button.

Deploy To Salesforce Button

Step 2

Select if you’re deploying the code to a Developer or Sandbox org.

Deploy to Location

Step 3

Press the “Login to Salesforce” button in the top right corner of the page and login into Salesforce.

Step 4

Press the “Deploy” button in the top right corner of the page. If all goes well, you should see a “Deployment Complete” status message.

Once the framework in installed, we honestly kind of ignore it. You can certainly read through the code if you’d like but I’ve never added or modified any of it.

Here’s a Simple Example

Let’s talk about how to use it. For our example, we’ll create a trigger on the Account object.

Create the Trigger Handler Class

We’ll start by creating a handler class for our Account object. We’ll call this AccountTriggerHandler. It’s a good practice to follow a consistent naming convention. The code looks like this:

public class AccountTriggerHandler extends TriggerHandler{

    public override void afterUpdate() {
        // do something
    }
      
    // add additional override methods for contexts such as beforeInsert, beforeUpdate, etc
}

The handler is where all the business logic will reside. It does not belong in the trigger. In the above example, I added trigger contexts for the afterUpdate; lines 3-5. You’ll want to repeat those lines for all other contexts which match your requirements.

Notice on line 1 we add “extends TriggerHandler”. This allows us to inherit all the methods and properties of the TriggerHandler framework. Basically, that means we don’t have to replicate the TriggerHandler code within our class and we can reuse it for all our trigger handlers.

So what does the “override” keyword mean on line 3 of our trigger context methods? How I understand it is that it simply override the method that is found in the TriggerHandler class. I’ll leave a more complete explanation to those that live and breathe code all day but for the rest of us, we can just use it as shown.

Don’t forget to save your class. We’ll come back to add our business logic later.

Create the Trigger

Now, let’s create our Account Trigger. This is is one of the reasons I really like this framework. The trigger is literally three lines of code that look like this:

trigger accountTrigger on Account (after update) {
    new AccountTriggerHandler().run();
}

It just doesn’t get much easier than this. Add any additional trigger contexts that you need like, before insert, before update, etc but other than that, this is all our trigger contains.

Line 2 simply says go run the AccountTriggerHandler class we created above. The TriggerHandler has the run() method and because it’s “extended” by our AccountTriggerHandler, the run() method executes.

Add our Business Logic

Let’s go back to the AccountTriggerHandler because we need to add our business logic.

Our requirement is that whenever the Account.OwnerId is changed, we need to update all the Contact.OwnerID to match. Yes, I know you can do this with clicks and not code but play along with my simple example. I would write this like this:

public class AccountTriggerHandler extends TriggerHandler{
 
    public AccountTriggerHandler() {
        this.setMaxLoopCount(1);
    }
 
    public override void afterUpdate() {
        map<Id, Id> ownerChangeMap = checkForOwnerChange();
        if (!ownerChangeMap.isEmpty()) updateContactOwners(ownerChangeMap);
    }
     
     
    private map<Id, Id> checkForOwnerChange() {
        map<Id, Id> newOwners = new map<Id, Id>();
        for (Account a : (list<Account>) trigger.new){
            Account oldAccount = (Account) trigger.oldMap.get(a.Id);
            if (oldAccount <> null &amp;&amp; oldAccount.OwnerId <> a.OwnerId){
                newOwners.put(a.Id, a.OwnerId);
            }
        }
        return newOwners;
    }
     
     
    private void updateContactOwners(map<Id, Id> ownerChangeMap){
        list<Contact> contactsToUpdate = new list<Contact>();
         
        list<Contact> contactList = [SELECT Id, AccountId, OwnerId FROM Contact WHERE AccountId in :ownerChangeMap.keySet()];
        for (Contact c : contactList){
            id newOwnerId = ownerChangeMap.get(c.AccountId);
            if (c.OwnerId <> newOwnerID){
                contactsToUpdate.add(new Contact(id=c.Id, ownerId=newOwnerId));
            }
        }
        if (!contactsToUpdate.isEmpty()) update contactsToUpdate;
    }
}

Starting with line 3, this method sets the limit for the number of times the trigger code will run. If you’re new to coding, you may not realize that triggers can often run multiple times within a single save transaction. This will prevent the logic from running unnecessarily.

I like to see the context methods, line 7-10, act as a sort of conductor. It simply calls other methods that do stuff. I like this because it makes it very simple to see what the code is doing. It’s also very helpful to name your methods according to what they do.

In our example, line 8, calls the checkForOwnerChange method. This method identifies if the Account.OwnerID has changed. If so, it collects those records and send them into the updateContactOwners method. The updateContactOwners method then makes the change to the actual Contact records.

Closing

Hopefully this working example helps you understand how to structure your trigger code.  If you have any questions or need assistance, please ask.  I’ll do my best to help you succeed.

Subscribe to Receive New Posts

Leave a Reply