Vinod Sebastian – B.Tech, M.Com, PGCBM, PGCPM, PGDBIO

Hi I'm a Web Architect by Profession and an Artist by nature. I love empowering People, aligning to Processes and delivering Projects.

Advertisements




Salesforce Triggers

Salesforce Trigger Overview

Salesforce triggers are automation actions that execute before or after records are inserted, updated, or deleted. They are essential for customizing and extending Salesforce functionality by allowing developers to write code that responds to specific events in Salesforce objects.

Salesforce Trigger Context Variables

In a Salesforce trigger, several context variables provide information about the trigger execution context:

  • Trigger.new: Contains a list of new records being inserted or updated.
  • Trigger.old: Contains a list of old versions of records being updated or deleted.
  • Trigger.newMap: A map of IDs to the new records.
  • Trigger.oldMap: A map of IDs to the old records.
  • Trigger.isInsert: Returns true if the trigger was fired due to an insert operation.
  • Trigger.isUpdate: Returns true if the trigger was fired due to an update operation.
  • Trigger.isDelete: Returns true if the trigger was fired due to a delete operation.
  • Trigger.isBefore: Returns true if the trigger was fired before the record was saved to the database.
  • Trigger.isAfter: Returns true if the trigger was fired after the record was saved to the database.

Please use before trigger for updating the record in same context and after trigger for related records.

Also it is best practice to have one trigger per object and bulkify code inside trigger.

Sample Code for a Salesforce Trigger

// Trigger: AccountTrigger.apxt
trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
    // Delegate logic to handler class
    AccountTriggerHandler.run(Trigger.isBefore, Trigger.isAfter, Trigger.isInsert, Trigger.isUpdate, Trigger.new, Trigger.oldMap);
}

Handling Recurrence of Triggers

Managing trigger recursion is crucial in Salesforce trigger development to prevent infinite loops and runtime errors. Here are some best practices:

  • Use Static Variables: Utilize static variables to track trigger recursion depth and maintain state.
  • Check Recursive Depth: Verify recursion depth against Salesforce limits before invoking trigger logic.
  • Disable Triggers: Temporarily disable triggers during bulk DML operations to avoid unnecessary recursion.
  • Implement Handler Classes: Organize trigger logic into handler classes for effective management.

Salesforce Trigger Framework

Building a robust trigger framework in Salesforce enhances code structure, readability, and maintainability. A well-designed trigger framework typically includes trigger handlers, service classes, and utility methods to execute business logic in response to database events.

Sample Code for a Salesforce Trigger Framework

public abstract class TriggerHandler<T> {
    private static Set<String> executed = new Set<String>();
    protected Boolean isEnabled = true;

    public void run(System.TriggerOperation op, List<T> newList, Map<Id, T> oldMap) {
        if (!isEnabled) return;

        String key = this.getClass().getName() + ':' + op;
        if (executed.contains(key)) return;
        executed.add(key);

        switch on op {
            when BEFORE_INSERT { beforeInsert(newList); }
            when BEFORE_UPDATE { beforeUpdate(newList, oldMap); }
            when BEFORE_DELETE { beforeDelete(oldMap); }
            when AFTER_INSERT { afterInsert(newList); }
            when AFTER_UPDATE { afterUpdate(newList, oldMap); }
            when AFTER_DELETE { afterDelete(oldMap); }
            when AFTER_UNDELETE { afterUndelete(newList); }
        }
    }

    protected virtual void beforeInsert(List<T> newList) {}
    protected virtual void beforeUpdate(List<T> newList, Map<Id, T> oldMap) {}
    protected virtual void beforeDelete(Map<Id, T> oldMap) {}
    protected virtual void afterInsert(List<T> newList) {}
    protected virtual void afterUpdate(List<T> newList, Map<Id, T> oldMap) {}
    protected virtual void afterDelete(Map<Id, T> oldMap) {}
    protected virtual void afterUndelete(List<T> newList) {}
}

By implementing a structured trigger framework like the one above, developers can ensure better organization and management of trigger logic.

Object-Specific Trigger Handler Example

Let’s consider an example of an object-specific trigger handler for the Account object:

public class AccountTriggerHandler extends TriggerHandler<Account> {
    protected override void beforeInsert(List<Account> newAccounts) {
        for (Account acc : newAccounts) {
            if (String.isBlank(acc.Name)) {
                acc.Name = 'Default Account Name';
            }
        }
    }

    protected override void beforeUpdate(List<Account> newAccounts, Map<Id, Account> oldMap) {
        for (Account acc : newAccounts) {
            Account oldAcc = oldMap.get(acc.Id);
            if (acc.Name != oldAcc.Name) {
                acc.Description = 'Name changed from ' + oldAcc.Name + ' to ' + acc.Name;
            }
        }
    }

    protected override void beforeDelete(Map<Id, Account> oldMap) {
        for (Account acc : oldMap.values()) {
            if (acc.Type == 'Critical') {
                acc.addError('Critical accounts cannot be deleted.');
            }
        }
    }

    protected override void afterInsert(List<Account> newAccounts) {
        System.debug('Inserted accounts: ' + newAccounts.size());
        // Additional logic can be implemented here
    }

    protected override void afterUpdate(List<Account> newAccounts, Map<Id, Account> oldMap) {
        System.debug('Updated accounts: ' + newAccounts.size());
        // Additional logic can be implemented here
    }

    protected override void afterDelete(Map<Id, Account> oldMap) {
        System.debug('Deleted accounts: ' + oldMap.size());
        // Additional logic can be implemented here
    }

    protected override void afterUndelete(List<Account> undeletedAccounts) {
        System.debug('Undeleted accounts: ' + undeletedAccounts.size());
        // Additional logic can be implemented here
    }
}

The AccountTriggerHandler class demonstrates how to handle various trigger events for Account records effectively.

Trigger (Lean & Exhaustive)

trigger AccountTrigger on Account (
    before insert, before update, before delete,
    after insert, after update, after delete, after undelete
) {
    new AccountTriggerHandler().run(Trigger.operationType, Trigger.new, Trigger.oldMap);
}

Best Practices for Salesforce Triggers

Adhering to best practices in Salesforce trigger development is crucial for delivering efficient, scalable, and reliable code solutions. Here are some recommended practices:

Best Practices for Salesforce Trigger Development

  • Keep Triggers Thin: Delegate complex business logic to handler classes or service methods to maintain trigger focus on database operations.
  • Bulkify Trigger Logic: Optimize trigger logic to handle bulk DML operations efficiently by using collections and SOQL queries.
  • Handle Exceptions Gracefully: Implement error handling in triggers to catch and manage exceptions effectively, enhancing code robustness.
  • Write Unit Tests: Ensure trigger logic reliability by writing comprehensive unit tests covering various scenarios and edge cases.
  • Document Trigger Logic: Document trigger functionality, input/output parameters, and dependencies to improve code readability and facilitate maintenance.