Apex Triggers controlled by Custom Metadata Types

Introduction

According to Salesforce documentation, Apex Triggers:

Enable you to perform custom actions before or after changes to Salesforce records, such as insertions, updates, or deletions. A trigger is Apex code that executes before or after the following types of operations: insert, update, delete, merge, upsert, undelete.

and Custom Metadata:

Is customizable, deployable, packageable, and upgradeable application metadata. First, you create a custom metadata type, which defines the form of the application metadata. Then you build reusable functionality that determines the behavior based on metadata of that type.

When to use?

You want to easily control invoking different contexts of Apex Triggers, for example during data migration.

Solution

Create a Custom Metadata record for each Apex Trigger and adjust the code. Then uncheck the checkbox to bypass specific Apex Trigger context.

Configure Custom Metadata

Create Custom Metadata object with checkbox fields for each Apex Trigger context and a text field for Apex Trigger class name or Apex Trigger Handler class name (if you are using Trigger Framework).

Apex Triggers 1

Create a Custom Metadata record for each Apex Trigger

CMD config 2

Update Apex class

If you have trigger context logic in Apex Trigger class, get Custom Metadata record and check if a specific context is bypassed.

//AccountTrigger class
trigger AccountTrigger on Account (before insert, after insert, before update, after update, before delete, after delete, after undelete) {
    Triggers_Configuration__mdt triggerConfiguration = [
            SELECT
                    Before_Insert__c, Before_Update__c, Before_Delete__c, After_Insert__c, After_Update__c, After_Delete__c, After_Undelete__c
            FROM
                    Triggers_Configuration__mdt
            WHERE
                    Trigger_API_Name__c = 'AccountTrigger'
            LIMIT 1
    ];

    if(Trigger.isBefore && Trigger.isInsert && triggerConfiguration.Before_Insert__c) {
        //Trigger logic
    }
}

Update Trigger Handler Class

If you are using Trigger Framework (https://github.com/kevinohara80/sfdc-trigger-framework), update Trigger Handler class to get Custom Metadata record and check if a specific context is bypassed.

//AccountTrigger class
trigger AccountTrigger on Account (before insert, after insert, before update, after update, before delete, after delete, after undelete) {
    new AccountTriggerHandler().run();
}
//AccountTriggerHandler class
public with sharing class AccountTriggerHandler extends TriggerHandler {

    private Map<Id, Account> newObjMap;
    private Map<Id, Account> oldObjMap;
    private List<Account> newObjList;
    private List<Account> oldObjList;

    public AccountTriggerHandler() {
        this.newObjMap = (Map<Id, Account>) Trigger.newMap;
        this.newObjList = (List<Account>) Trigger.new;
        this.oldObjMap = (Map<Id, Account>) Trigger.oldMap;
        this.oldObjList = (List<Account>) Trigger.old;
    }

    public override void beforeInsert() {
        //Trigger logic
    }
}
//part of TriggerHandler class
public void run() {

        if(!validateRun()) {
            return;
        }

        addToLoopCount();

        Triggers_Configuration__mdt triggerConfiguration = [
                SELECT
                        Before_Insert__c, Before_Update__c, Before_Delete__c, After_Insert__c, After_Update__c, After_Delete__c, After_Undelete__c
                FROM
                        Triggers_Configuration__mdt
                WHERE
                        Trigger_Handler_API_Name__c = getHandlerName()
                LIMIT 1
        ];

        // dispatch to the correct handler method
        if(this.context == TriggerContext.BEFORE_INSERT && triggerConfiguration.Before_Insert__c) {
            this.beforeInsert();
        } else if(this.context == TriggerContext.BEFORE_UPDATE && triggerConfiguration.Before_Update__c) {
            this.beforeUpdate();
        } else if(this.context == TriggerContext.BEFORE_DELETE && triggerConfiguration.Before_Delete__c) {
            this.beforeDelete();
        } else if(this.context == TriggerContext.AFTER_INSERT && triggerConfiguration.After_Insert__c) {
            this.afterInsert();
        } else if(this.context == TriggerContext.AFTER_UPDATE && triggerConfiguration.After_Update__c) {
            this.afterUpdate();
        } else if(this.context == TriggerContext.AFTER_DELETE && triggerConfiguration.After_Delete__c) {
            this.afterDelete();
        } else if(this.context == TriggerContext.AFTER_UNDELETE && triggerConfiguration.After_Undelete__c) {
            this.afterUndelete();
        }
    }

private String getHandlerName() {
    return String.valueOf(this).substring(0,String.valueOf(this).indexOf(':'));
}

Resources

5 3 votes
Article Rating
Subscribe
Notify of
guest
2 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Karl
Karl
1 year ago

This works great for Accounts, but how would we modify it to work with all Sobjects. Basically, how could we insert the Sobject type for the Trigger_API_Name__c based on the Trigger’s Sobject type? For example, I create a custom meta refcord for a ContactTrigger.

Also, it looks like your text editor substituted && for &&

Close Menu
2
0
Would love your thoughts, please comment.x
()
x