Understanding trigger context variables is essential for any Salesforce developer working with Apex triggers. These variables provide valuable information about the state of records and the context in which the trigger is executing. In this blog post, we’ll explore the various trigger context variables, how to use them effectively, and best practices for leveraging them in your Apex triggers.
Trigger context variables are special variables that are available within Apex triggers. They provide context about the records and the operation that is being performed. These variables help you write logic that responds appropriately to different trigger events and scenarios.
Here are some of the most commonly used trigger context variables:
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 is a before trigger.Trigger.isAfter
: Returns true
if the trigger is an after trigger.Trigger.new
: Returns a list of the new versions of the sObject records.Trigger.old
: Returns a list of the old versions of the sObject records.Trigger.newMap
: Returns a map of IDs to the new versions of the sObject records.Trigger.oldMap
: Returns a map of IDs to the old versions of the sObject records.Trigger.size
: Returns the total number of records in the trigger invocation.Let’s explore how to use these context variables in your Apex triggers with practical examples.
You can use context variables to determine the type of operation that caused the trigger to fire.
trigger ContactTrigger on Contact (before insert, before update, before delete) {if (Trigger.isInsert) {// Logic for insert operation} else if (Trigger.isUpdate) {// Logic for update operation} else if (Trigger.isDelete) {// Logic for delete operation}}
In update triggers, you might need to compare the new and old values of records.
trigger OpportunityTrigger on Opportunity (before update) {for (Opportunity opp : Trigger.new) {Opportunity oldOpp = Trigger.oldMap.get(opp.Id);// Check if the 'StageName' field has changedif (opp.StageName != oldOpp.StageName) {// Implement logic when the stage changes}}}
Trigger.new
and Trigger.old
Trigger.new
and Trigger.old
are available in different trigger contexts:
Trigger.new
: Available in before
and after
insert and update triggers, and undelete triggers.Trigger.old
: Available in update
and delete
triggers.Using Trigger.newMap
and Trigger.oldMap
provides efficient access to records by their IDs.
trigger AccountTrigger on Account (before update) {Map<Id, Account> oldAccounts = Trigger.oldMap;for (Account acc : Trigger.new) {Account oldAcc = oldAccounts.get(acc.Id);// Compare old and new valuesif (acc.Name != oldAcc.Name) {// Name has changed}}}
Always design your triggers to handle multiple records efficiently.
trigger LeadTrigger on Lead (after insert) {List<Task> tasksToCreate = new List<Task>();for (Lead lead : Trigger.new) {Task task = new Task(Subject = 'Follow up',WhoId = lead.Id,OwnerId = lead.OwnerId);tasksToCreate.add(task);}if (!tasksToCreate.isEmpty()) {insert tasksToCreate;}}
Use dynamic methods to reference metadata instead of hardcoding.
// Avoid hardcodingString recordTypeId = '01230000000ABC';// Use dynamic retrievalString recordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByName().get('Business').getRecordTypeId();
Ensure your trigger logic executes only under the correct conditions.
if (Trigger.isBefore && Trigger.isInsert) {// Logic specific to before insert}
Always check for null values to prevent runtime exceptions.
for (Case c : Trigger.new) {if (c.ContactId != null) {// Logic when ContactId is not null}}
Trigger.size
Use Trigger.size
to get the number of records being processed.
System.debug('Number of records in this trigger: ' + Trigger.size);
Knowing the order of execution helps in designing triggers that work harmoniously with other automation tools like workflows and process builders.
Triggers that update records can cause themselves to re-execute indefinitely.
Solution: Implement logic to prevent recursion.
public class TriggerUtility {public static Boolean isFirstRun = true;}trigger ContactTrigger on Contact (before update) {if (TriggerUtility.isFirstRun) {TriggerUtility.isFirstRun = false;// Your logic here}}
Performing SOQL queries or DML operations inside loops can quickly exceed Salesforce governor limits.
Solution: Move queries and DML operations outside loops.
// Bad Practicefor (Account acc : Trigger.new) {List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];// Process contacts}// Best PracticeSet<Id> accountIds = new Set<Id>();for (Account acc : Trigger.new) {accountIds.add(acc.Id);}List<Contact> contacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds];// Process contacts outside the loop
Writing robust unit tests ensures your triggers function as expected.
@isTestprivate class OpportunityTriggerTest {@isTest static void testStageChange() {Opportunity opp = new Opportunity(Name = 'Test Opportunity',StageName = 'Prospecting',CloseDate = Date.today().addDays(30));insert opp;opp.StageName = 'Qualification';update opp;// Retrieve the updated opportunityOpportunity updatedOpp = [SELECT StageName FROM Opportunity WHERE Id = :opp.Id];System.assertEquals('Qualification', updatedOpp.StageName);}}
Trigger context variables are powerful tools that provide critical information about the execution context of your Apex triggers. By mastering these variables, you can write more efficient, reliable, and maintainable code.
Key Takeaways:
Trigger.new
and Trigger.old
to access the new and old versions of records.By applying these principles, you’ll enhance your ability to create sophisticated automation solutions within Salesforce.
Learn more about Apex Triggers
Happy coding on your Salesforce journey!
Quick Links
Legal Stuff