Tag Archives: Apex Trigger

Salesforce Apex TriggerOperation Enum

The System.TriggerOperation enum has the following values:

  • BEFORE_INSERE
  • AFTER_INSERT
  • BEFORE_UPDATE
  • AFTER_UPDATE
  • BEFORE_DELETE
  • AFTER_DELETE
  • AFTER_UNDELETE

The new Trigger context variable Trigger.operationType will return System.TriggerOperation enum during trigger context.

If you combine this new context variable and the new Apex switch feature, trigger code becomes much easy to implement and understand.

Sample Switch and enum in Triggers:

trigger AccountTrigger on Account(before insert, after insert, before update, after update, before delete, after delete, after undelete) {
    
    switch on Trigger.operationType {
        
        when BEFORE_INSERT {
            //Invoke before insert trigger handler
            system.debug('Before Insert');
        }
        when AFTER_INSERT {
            //Invoke after insert trigger handler
            system.debug('After Insert');
        }
        when BEFORE_UPDATE {
            //Invoke before update trigger handler
            system.debug('Before Update');
        }
        when AFTER_UPDATE {
            //Invoke after update trigger handler
            system.debug('After Update');
        }
        when BEFORE_DELETE {
            //Invoke before delete trigger handler
            system.debug('Before Delete');
        }
        when AFTER_DELETE {
            //Invoke after delete trigger handler
            system.debug('After Delete');
        }
        when AFTER_UNDELETE {
            //Invoke after undelete trigger handler
            system.debug('After Undelete');
        }
    }
}

Delete Apex Class or Trigger in Salesforce Production Org Using Eclipse

It is not possible to directly delete an Apex class or trigger after it has been deployed to production. Here are the steps to delete apex class or trigger in Salesforce Production Org by using eclipse and Force.com IDE.

  • Download and Install the Force.com IDE for Eclipse.
  • Connect to the Salesforce Production org.
  • Download the apex class/trigger.
  • Open the meta.xml file of the Apex class/trigger.
  • Change the status of the Apex class/trigger to Deleted.
  • Save and deploy to server.

Note: Apex class status can only be changed to “Active” or “Deleted” and not “Inactive”.

Avoid Recursive Trigger Calls In Salesforce

Recursion occurs when same code is executed again and again. It can lead to infinite loop and which can result to governor limit sometime. Sometime it can also result in unexpected output or the error “maximum trigger depth exceeded”. So, we should write code in such a way that it does not result to recursion.

For example, I’ve a trigger on Account object, which will be execute on before Update and after Update. In after Update I’ve some custom logic to update Account records. So, when I’m updating the Account records in After Update, it is throwing error “maximum trigger depth exceeded”. So, it is a recursion in apex trigger.

To avoid the situation of recursive call, we have to write code in such a way that the trigger will execute one time. To do so, we can create a class with a static Boolean variable with default value true. In the trigger, before executing the code keep a check that the variable is true or not.

Here in below trigger, I want to execute both before and after Update trigger only one time. I’m checking the static Boolean variable is true in both before and after Update trigger. In after update trigger changed the variable to false and after updating the account records, again changed the variable to true. In this way you can handle bulk of records in recursive trigger.

Apex Class:

public Class AccountTriggerHelper{
    //Trigger execution check variable
    public static Boolean runOnce = true;
    
    //On before update
    public void onBeforeUpdate(map<Id, Account> mapNewAccount, map<Id, Account> mapOldAccount){
        //Write your logic here
        System.debug('Is Before Update');
    }
    
    //On after update
    public void onAfterUpdate(map<Id, Account> mapNewAccount, map<Id, Account> mapOldAccount){
        Update [SELECT Id, Name FROM Account WHERE Id IN: mapNewAccount.keyset()];
        AccountTriggerHelper.runOnce = true;
        System.debug('Is After Update');
    }
}

Apex Trigger:

trigger AccountTrigger on Account(before Update, after Update) {
    
    AccountTriggerHelper helper = new AccountTriggerHelper();
    
    if(Trigger.isBefore && Trigger.isUpdate && AccountTriggerHelper.runOnce){
        helper.onBeforeUpdate(Trigger.newMap, Trigger.OldMap);     
    }
    
    if(Trigger.isAfter && Trigger.isUpdate && AccountTriggerHelper.runOnce){
        AccountTriggerHelper.runOnce = false;
        helper.onAfterUpdate(Trigger.newMap, Trigger.OldMap);
    }
}

Debug Log:

Check Case Owner is a User or Queue

Check Case Owner in Apex Class.

//Check object Id in Apex 
if(string.valueOf(c.OwnerId).startsWith('005')){
    //Owner is User       
}

if(string.valueOf(c.OwnerId).startsWith('00G')){
    //Owner is Queue
}

Check Case Owner in Apex Trigger.

//In Apex Trigger
for (Case objCase : Trigger.new) { 
    If (objCase.OwnerID.getsobjecttype() == User.sobjecttype) {
        /*Code if Owner is User*/
    }
    else{
        /*Code if Owner is Queue*/ 
    }
}

Check Case Owner by SOQL query.

//By Query Owner.Type Field
List<Case> caseList = [SELECT Id, CaseNumber, OwnerId, Owner.Name, Owner.Type FROM Case];

for (Case objCase : caseList){
    If (objCase.Owner.Type == User.sobjecttype) {
        /*Code if Owner is User*/
    }
    else{
        /*Code if Owner is Queue*/ 
    }
}

Check Case Owner in Process Builder.

//Check in Process Builder
BEGINS([Case].OwnerId, "005") //Check Owner is User
BEGINS([Case].OwnerId, "00G") //Check Owner is Queue

Create One to One Relationship in Salesforce

Basically Salesforce offers two types of relationship:

  • One To Many Relationship
  • Many To Many Relationship (Using the concept of Junction object)

Sometimes we may need One To One relationship, But unfortunately Salesforce doesn’t allow any direct methodology to build One To one relationship.

Let’s consider the scenario that we want to establish a One to One relationship between two custom objects Employee__c and PAN_Card__c.

So, here are few ways to implement One To One relationship between two objects in Salesforce. We can achieve this by using configuration only and can also achieve this by using code to make it more scalable.

Option 1:

  • Create a lookup field on PAN_Card__c to Employee__c.
  • Create a custom field on the PAN_Card__c object and make the field unique. This field would be used to hold the ID of the associated Employee__c. Hide this field from all page layouts.
  • Create a Workflow rule on PAN_Card__c. For any change of the lookup field on PAN_Card__c object, update the custom field on the PAN_Card__c object with the value of the associated Employee Id.

We have established a one to one relationship between PAN_Card__c and Employee__c. When we try to add a second PAN_Card__c to the Employee__c, the “unique” constraint would be violated and an error would be thrown. This approach will work on both standard and custom object.

Option 2:

  • Create a master detail relationship on PAN_Card__c to Employee__c object.
  • Create a roll up summary field on Employee__c object of PAN_Card__c with count type.
  • Create a validation rule on Employee__c object rollup summary field to check if count > 1.

In this way also, We have established a one to one relationship between PAN_Card__c and Employee__c. So it will throw an error if Employee__c has more than one PAN Card.

Option 3:

  • Create lookup fields on both objects PAN_Card__c and Employee__c, to each other.
  • Write triggers, for any change on these lookups, to either copy the record ID into the other object’s lookup field when the other object’s lookup field is empty, or disallow the change to the original record when the other object’s lookup field is already populated with a different ID from the original record.

This is already having a one-to-onePassport relation.

Option 4:

  • Create a trigger on PAN_Card__c object to check if the PAN Card record already exists for an Employee. If it exist, then throw an error, else allow the user to create.
    Here is the example for Employee__c and PAN_Card__c object:

    trigger PANCardValidation on PAN_Card__c (before insert, before update) {
        Set<id> employeeIds = new Set<id>();
        Map<id, Employee__c> mapEmployee = new Map<id, Employee__c>();
     
        for (PAN_Card__c p : trigger.New) {
            employeeIds.add(p.Employee__c);
        }
     
        List<Employee__c> lstEmployee = [SELECT Id, Name FROM Employee__c WHERE Id IN : employeeIds];
        if (!lstEmployee.isEmpty()) {
            for (Employee__c emp : lstEmployee) {
                mapEmployee.put(emp.Id, emp);
            }
     
            for (PAN_Card__c p : trigger.New) {
                if (mapEmployee.containsKey(p.Employee__c)) {
                    p.addError('A PAN Card already exist for the employee - ' + mapEmployee.get(p.Employee__c).Name);
                }
            }
        }
    }