Tag Archives: Apex Trigger

Bulkify Apex Trigger in Salesforce

Bulkifying Apex Trigger refers to the concept of making sure the code properly handles more than one record at a time. When a batch of records initiates Apex, a single instance of that Apex code is executed, but it needs to handle all of the records in that given batch.

For example, a trigger could be invoked by an Force.com SOAP API call that inserted a batch of records. So if a batch of records invokes the same Apex trigger, all of those records need to be processed as a bulk, in order to write scalable code and avoid hitting governor limits.

Here is a simple example of some Apex code both bulkified, and not bulkified.

Not Bulkified:

trigger AccountTriggr on Account (before update) {
    
    //This only handles the first record in the Trigger.new collection
    //But if more than one Account initiated this trigger, those additional records
    //will not be processed
    Account acc = Trigger.new[0];
    List<Contact> conList = [SELECT Id, FirstName, LastName, Email, Phone FROM Contact WHERE AccountId = :acc.Id];
}

In above apex trigger the issue is that only one Account record is handled because the code explicitly accesses only the first record in the Trigger.new collection by using Trigger.new[0]. Instead, the trigger should properly handle the entire collection of Accounts in the Trigger.new collection.

Bulkified:

trigger AccountTriggr on Account (before update) {
    
    //Loop through all records in the Trigger.new collection
    for(Account a: Trigger.new){
        //Write your logic
    }
}

In above bulkified apex trigger the code iterates across the entire Trigger.new collection with a for loop. Now if this trigger is invoked with a single Account or up to 200 Accounts, all records are properly processed.

Call Batch Apex From Apex Trigger

Batch Apex Class:

global class AccountBatchApex implements database.batchable<sobject>{
    
    global database.querylocator start(database.batchableContext bc){
        String query = 'SELECT Name, AccountNumber, Type From Account';
        return database.getquerylocator(query);
    }
    
    global void execute(database.batchablecontext bd, List<Account> scope){
        for(Account acc: scope){
            //Write your logic
        }  
    }
    
    Public void finish(database.batchableContext bc){ 
    }
}

Apex Trigger:

trigger AccountTrigger on Account (after insert) {
    List<Account> accList = new List<Account>();
    for(account acc : trigger.new){
        if(acc.Type.equals('Customer - Direct')){
            accList.add(acc);
        }
    }
    
    if(accList.size() > 0){
        AccountBatchApex objBatch = new AccountBatchApex();
        Database.executebatch(objBatch,200);
    }
}

Salesforce Trigger Handler Patterns

To move all code from Trigger to Apex Classes(Handler Classes) is one of the Salesforce Best Practices. Here is an example to write Trigger Handler class.

Apex Trigger:

trigger objectTrigger on Object (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
    
    objectTriggerHandler handler = new objectTriggerHandler();
    
    //Before Insert
    if(Trigger.isInsert && Trigger.isBefore){
        handler.OnBeforeInsert(Trigger.new);
    }
    //After Insert
    else if(Trigger.isInsert && Trigger.isAfter){
        handler.OnAfterInsert(Trigger.new);
    }
    //Before Update
    else if(Trigger.isUpdate && Trigger.isBefore){
        handler.OnBeforeUpdate(Trigger.old, Trigger.new, Trigger.newMap);
    }
    //After Update
    else if(Trigger.isUpdate && Trigger.isAfter){
        handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.newMap);
    }
    //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);
    }
}

Trigger Handler Apex Class:

public with sharing class ObjectTriggerHandler {
    
    private boolean isExecuting = false;
    
    public ObjectTriggerHandler(boolean isExecuting){
        this.isExecuting = isExecuting;
    }
    
    public void OnBeforeInsert(List<Object> newObjects){
        //EXECUTE BEFORE INSERT LOGIC
    }
    
    public void OnAfterInsert(List<Object> newObjects){
        //EXECUTE AFTER INSERT LOGIC
    }
    
    public void OnBeforeUpdate(List<Object> oldObjects, List<Object> updatedObjects, Map<Id, Object> ObjectMap){
        //BEFORE UPDATE LOGIC
    }
    
    public void OnAfterUpdate(List<Object> oldObjects, List<Object> updatedObjects, Map<Id, Object> ObjectMap){
        //AFTER UPDATE LOGIC
    }
    
    public void OnBeforeDelete(List<Object> ObjectsToDelete, Map<Id, Object> ObjectMap){
        //BEFORE DELETE LOGIC
    }
    
    public void OnAfterDelete(List<Object> deletedObjects, Map<Id, Object> ObjectMap){
        //AFTER DELETE LOGIC
    }
    
    public void OnUndelete(List<Object> restoredObjects){
        //AFTER UNDELETE LOGIC
    }
    
    public boolean IsTriggerContext{
        get{ return isExecuting;}
    }
}

Different Ways of Making a Field Mandatory in Salesforce

There are 4 ways of making the field mandatory:

Page Layout: Field can be made mandatory from the page layout when it needs to be made mandatory for a set of users.
Field Level Security: Field can be made mandatory from the FLS when it needs to be made mandatory for all the users in the Organization and even from the API’s.
Validation Rule: Field can be made mandatory from the Validation Rule, when it needs to be made mandatory for user who is using the same Page layout used by other users.
Before Triggers: we can also make a field mandatory using before trigger

Note: Salesforce.com recommends using the Page Layout option for making the field mandatory.