0% found this document useful (0 votes)
192 views18 pages

Apex Triggers Cheatsheet

Doc

Uploaded by

aveshsingh172
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
192 views18 pages

Apex Triggers Cheatsheet

Doc

Uploaded by

aveshsingh172
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

APEX TRIGGERS

Rajaganesh Saravanan

The trigger is an Apex Code that executes before or after changes occurs to Salesforce records.

These changes includes operations like Insert, Update, Delete, Merge, Upsert and Undelete.

WHEN TO USE TRIGGERS:


1. An Apex Trigger is a Stored Apex procedure that is called whenever a record is inserted, updated,

deleted, or undeleted. If any change happens to a single or multiple records, the Apex Trigger

created on that object will be fired.

2. For example, we can have a trigger run:

Before an object’s record is inserted into the database.


After a record has been deleted.
Even after a record is restored back from recycle bin.

3. Use triggers to perform tasks that can’t be done using a point & click tool in Salesforce. If the

task is possible using point & click tools then always prefer doing it from them.

4. Triggers are active by-default when created and Salesforce automatically fires active triggers

when specified database event occurs.

TYPES OF APEX TRIGGERS:


1. Before Triggers: These are used to update/modify or validate records before they are saved to

database.

2. After Triggers: These are used to access fields values that are set by the system like recordId,

lastModifiedDate field. Also, we can make changes to other records in the after trigger but not on

the record which initiated/triggered the execution of after trigger because the records that fire

the after trigger are read-only.


Syntax & Trigger Events:

trigger TriggerName on ObjectName (trigger_events)

//code-block

where trigger-events can be a comma-separated list of one or more of the following events:

● Before insert

● Before update

● Before delete

● After insert

● After update

● After delete

● After undelete

Example:

Trigger firstTrigger on Account(before insert)

System.debug(‘I am before insert.’);

Trigger secondTrigger on Account(after insert)

System.debug(‘I am after insert.’);

For example:

Update Account a;

(Before update Trigger of Account a)

Inserts contact b;
}

(after insert of contact B)

Insert a; // over here we are updating Account a in its before trigger

// indirectly i.e. not allowed.

Note: If you update or delete a record in its before trigger, or delete a record in its after trigger you’ll

receive an error and this includes both direct and indirect operations.

Salesforce Order of Execution Stages


Stage 1: Load the Data

Salesforce loads or initializes the record from the database. Salesforce also loads
the new record with the provided new values by overriding the old values.

Stage 2: Identify the Request

Salesforce identifies the source of the request. The source can be standard UI
pages, custom UI pages, SOAP Api or REST api. Based on the source of the request,
different validation checks are performed.

Stage 3: Execute Record Triggered Flows

Salesforce executes Record Triggered flows which are created to run before the
record is saved.

Stage 4: Execute Before Triggers

Salesforce executes all the Before triggers i.e. before insert or before update or
before delete triggers. So, any logic written in the Before triggers will execute at
this stage.

Stage 5: Validate the Data

Once the Before Triggers have been executed, Salesforce system validation
mechanisms execute all the system and custom validations.

Stage 6: Duplicate Rules


After the validation, Salesforce runs the Duplicates rules on the record in context.
The most important thing is if the Duplicate rules are configured with the Block
action, then Order of execution will stop at this stage.

Stage 7: Save the Record

After the Duplicates rules, Salesforce will Save the record but will not commit it to
the database. Record will only be committed when the record in context has
successfully completed the order of execution without any failure.

Stage 8: Execute After Triggers

After the record is saved, Salesforce executes any After Triggers i.e. after insert,
after update, after delete So, any logic written in the After triggers will execute at
this stage

Stage 9: Execute Assignment Rules

If the record is an object that supports assignment rules, Salesforce executes the
Assignment Rules at this stage. Assignment rules specify how records should be
assigned to users or queues. These rules can be based on various criteria, such as
record type, region, or priority.

Stage 10: Execute Auto-Response Rules

If the record is an object that supports auto-response rules, Salesforce executes


the Auto-Response Rules at this stage. Auto-Response Rules specify how records
should be automatically responded to, such as by sending an email or a text
message.

Stage 11: Execute Workflow Rules – If workflow field updates occur:

Perform another update on the record.


Re-run system validations. Note that custom validation rules, flows, duplicate
rules, processes, and escalation rules will not be re-executed.
Execute before and after update triggers once more, irrespective of whether
it’s an insert or update operation, but only once.

Stage 12: Escalations Rules

If the record is an object that supports Escalation Rules, Salesforce executes the
Escalation Rules at this stage. Escalation rules specify how records should be
escalated to higher authorities, such as managers or executives if they are not
processed within a certain time frame.
Stage 13: Execute Flows

If the record is an object that supports Lightning Flows and Processes, Salesforce
executes them at this stage. The order in which Flows will execute is as per system
behavior and your configuration.

Stage 14: Execute After Record Triggered Flows

At this stage, Salesforce runs the record-triggered flows which are created to run
after the record is saved.

Stage 15: Execute Entitlement Rules

If the record is an object that supports Entitlement Rules, Salesforce executes the
Entitlement Rules at this stage.

Stage 16: Rollup Summary Fields

If the parent record or grand parent records contain rollup summary fields, then
once the value is calculated the record goes through the save procedure cycle
again.

Stage 17: Criteria-Based Sharing Rules

At this stage, Salesforce will check and execute all the Criteria Based Sharing Rules

for all the records in context to make sure the records are shared correctly.

Stage 18: Record Commit

After finishing all the above events on the server, Salesforce will finally commit the
record in the database.

Stage 19: Post-Commit Logic

Once all the previous stages have been completed, Salesforce executes any Post-
Commit Logic. Post-Commit Logic includes any custom code blocks developers
have created to run after a record has been committed to the database. These
code blocks can perform various actions, such as generating reports, updating
external systems, or sending notifications, sending emails, invoking async jobs, etc.
Context Variables

Trigger.New contains all the records that were inserted in insert or update triggers.

Trigger.Old provides the old version of sObjects before they were updated in update triggers, or a list
of deleted sObjects in delete triggers

➔ Trigger.new and Trigger.old cannot be used in Apex DML Operations.

➔ You can use an sObject to change its own field values using trigger.new but only in before triggers.

➔ Trigger.old is always read-only.

➔ You can not delete Trigger.new.

➔ Upsert and merge events do not have their own triggers, instead they fire other triggers based on

the events as a result of merge operation.


Bulk Apex Triggers
When you use bulk design patterns, your triggers have better performance,

consume less server resources, and are less likely to exceed platform limits.

The benefit of bulkifying your code is that bulkified code can process large

numbers of records efficiently and run within governor limits on the Lightning

Platform. These governor limits are in place to ensure that runaway code

doesn’t monopolize resources on the multitenant platform.

Not Bulk

trigger MyTriggerNotBulk on Account(before insert) {

Account a = Trigger.New[0];

a.Description = 'New description';

Bulk (which is better)

trigger MyTriggerBulk on Account(before insert) {

for(Account a : Trigger.New) {

a.Description = 'New description';

Bulk SOQL Queries

Use this to avoid Query limits which are 100 SOQL queries for synchronous Apex or

200 for asynchronous Apex.

Bad Practice

trigger SoqlTriggerNotBulk on Account(after update) {

for(Account a : Trigger.New) {

// Get child records for each account

// Inefficient SOQL query as it runs once for each account!


Opportunity[] opps = [SELECT Id,Name,CloseDate

FROM Opportunity WHERE AccountId=:a.Id];

// Do some other processing

Good Practice

Store to a list first before iteration

trigger SoqlTriggerBulk on Account(after update) {

// Perform SOQL query once.

// Get the accounts and their related opportunities.

List<Account> acctsWithOpps = [SELECT Id,(SELECT Id,Name,CloseDate FROM Opportunities) FROM


Account WHERE Id IN :Trigger.New];

// Iterate over the returned accounts

for(Account a : acctsWithOpps) {

Opportunity[] relatedOpps = a.Opportunities;

// Do some other processing

Alternative, when Account parent records are not needed

trigger SoqlTriggerBulk on Account(after update) {

// Perform SOQL query once.

// Get the related opportunities for the accounts in this trigger.

List<Opportunity> relatedOpps = [SELECT Id,Name,CloseDate FROM Opportunity

WHERE AccountId IN :Trigger.New];

// Iterate over the related opportunities

for(Opportunity opp : relatedOpps) {

// Do some other processing


}

Improve the Alternative with a For-loop in one statement

trigger SoqlTriggerBulk on Account(after update) {

// Perform SOQL query once.

// Get the related opportunities for the accounts in this trigger,

// and iterate over those records.

for(Opportunity opp : [SELECT Id,Name,CloseDate FROM Opportunity

WHERE AccountId IN :Trigger.New]) {

// Do some other processing

Triggers execute on batches of 200 records at a time. So if 400 records cause a

trigger to fire, the trigger fires twice, once for each 200 records. For this reason, you

don’t get the benefit of SOQL for loop record batching in triggers, because triggers

batch up records as well. The SOQL for loop is called twice in this example, but a

standalone SOQL query would also be called twice. However, the SOQL for loop still

looks more elegant than iterating over a collection variable!

Bulk DML

Use this because the Apex runtime allows up to 150 DML calls in one transaction.

Bad Practice

trigger DmlTriggerNotBulk on Account(after update) {

// Get the related opportunities for the accounts in this trigger.

List<Opportunity> relatedOpps = [SELECT Id,Name,Probability FROM Opportunity

WHERE AccountId IN :Trigger.New];

// Iterate over the related opportunities


for(Opportunity opp : relatedOpps) {

// Update the description when probability is greater

// than 50% but less than 100%

if ((opp.Probability >= 50) && (opp.Probability < 100)) {

opp.Description = 'New description for opportunity.';

// Update once for each opportunity -- not efficient!

update opp;

Good Practice

Store records to a list first before updating. Don't update per record.

trigger DmlTriggerBulk on Account(after update) {

// Get the related opportunities for the accounts in this trigger.

List<Opportunity> relatedOpps = [SELECT Id,Name,Probability FROM Opportunity

WHERE AccountId IN :Trigger.New];

List<Opportunity> oppsToUpdate = new List<Opportunity>();

// Iterate over the related opportunities

for(Opportunity opp : relatedOpps) {

// Update the description when probability is greater

// than 50% but less than 100%

if ((opp.Probability >= 50) && (opp.Probability < 100)) {

opp.Description = 'New description for opportunity.';

oppsToUpdate.add(opp);

// Perform DML on a collection


update oppsToUpdate;

Adding Related Records from a Trigger (IMPROVED)

Instead of checking an opportunities list size for each record in the loop, add the

condition in the main query.

trigger AddRelatedRecord on Account(after insert, after update) {

List<Opportunity> oppList = new List<Opportunity>();

Add an opportunity for each account if it doesn't already have one.

Iterate over accounts that are in this trigger but that don't have

opportunities.

for (Account a : [SELECT Id,Name FROM Account

WHERE Id IN :Trigger.New AND

Id NOT IN (SELECT AccountId FROM Opportunity)]) {

Add a default opportunity for this account

oppList.add(new Opportunity(Name=a.Name + ' Opportunity',

StageName='Prospecting',

CloseDate=System.today().addMonths(1),

AccountId=a.Id));

if (oppList.size() > 0) {

insert oppList;

1. After Update Trigger on Parent Object (Update Child Record)

Scenario: When the Parent record is updated, update specific fields on related Child records.
Solution:

Use an after update trigger on the Parent object to update all related Child records in a
single DML statement.
Avoid querying and updating within nested loops.

Example: Let's say you have an Account (Parent) object and a related Contact (Child) object.
When an Account's Industry field is updated, you want to update the Industry__c field on all
related Contact records.

apex

Copy code

trigger AccountAfterUpdate on Account (after update) {

Set<Id> accountIds = new Set<Id>();

for (Account acc : Trigger.new) {

if (acc.Industry != Trigger.oldMap.get(acc.Id).Industry) {

accountIds.add(acc.Id);

if (!accountIds.isEmpty()) {

List<Contact> contactsToUpdate = new List<Contact>();

for (Contact con : [SELECT Id, AccountId FROM Contact WHERE AccountId IN
:accountIds]) {

con.Industry__c = Trigger.newMap.get(con.AccountId).Industry;

contactsToUpdate.add(con);

if (!contactsToUpdate.isEmpty()) {
update contactsToUpdate;

Best Practices:

Minimize SOQL queries within loops.


Collect Ids for bulk operations and ensure only the necessary fields are updated.

2. After Insert Trigger on Parent Object (Insert New Child Records)

Scenario: When a new Parent record is inserted, automatically create Child records
associated with it.

Solution:

In an after insert trigger, gather the Parent record data, then use a bulk insert for the
related Child records.

Example: Suppose each new Account should have two associated Contact records by default.

apex

Copy code

trigger AccountAfterInsert on Account (after insert) {

List<Contact> contactsToInsert = new List<Contact>();

for (Account acc : Trigger.new) {

contactsToInsert.add(new Contact(LastName = 'Primary', AccountId = acc.Id));

contactsToInsert.add(new Contact(LastName = 'Secondary', AccountId = acc.Id));

}
if (!contactsToInsert.isEmpty()) {

insert contactsToInsert;

Best Practices:

Avoid creating Child records in a loop by using a single bulk insert statement after
collecting the records.
Ensure trigger logic is bulkified to handle multiple Parent records.

3. After Update Trigger on Child Object (Update Parent Record)

Scenario: When a Child record is updated, update specific fields on the related Parent record.

Solution:

Use a Map<Id, ParentObject> to update the Parent only once per Parent record, even if
multiple Child records trigger the update.

Example: Imagine a Contact (Child) update that should modify the related Account (Parent)
field Has_Active_Contacts__c.

apex

Copy code

trigger ContactAfterUpdate on Contact (after update) {

Map<Id, Account> accountsToUpdate = new Map<Id, Account>();

for (Contact con : Trigger.new) {

if (con.IsActive__c != Trigger.oldMap.get(con.Id).IsActive__c) {

Account acc = new Account(Id = con.AccountId);

acc.Has_Active_Contacts__c = true; // Example logic


accountsToUpdate.put(acc.Id, acc);

if (!accountsToUpdate.isEmpty()) {

update accountsToUpdate.values();

Best Practices:

Avoid querying Parent records unless necessary by using new Account(Id =


con.AccountId) directly in the map.
Use a Map to handle bulk updates efficiently.

4. Roll-Up Summary Trigger (Simulating Roll-Up Summary for Non-Master-


Detail Relationships)

Scenario: Implement a roll-up summary where the Parent and Child are not in a Master-Detail
relationship.

Solution:

Use a trigger on the Child object to calculate values and store them in a Map<Id,
AggregateValue>.
Update the Parent record once per unique Parent ID.

Example: If you want to count the total Active_Contacts__c on an Account based on related
Contact records:

apex

Copy code
trigger ContactAfterInsertUpdateDelete on Contact (after insert, after update, after delete,
after undelete) {

Map<Id, Integer> accountContactCountMap = new Map<Id, Integer>();

for (Contact con : [SELECT AccountId FROM Contact WHERE AccountId != NULL]) {

accountContactCountMap.put(con.AccountId,
accountContactCountMap.getOrDefault(con.AccountId, 0) + 1);

List<Account> accountsToUpdate = new List<Account>();

for (Id accountId : accountContactCountMap.keySet()) {

accountsToUpdate.add(new Account(Id = accountId, Active_Contacts__c =


accountContactCountMap.get(accountId)));

if (!accountsToUpdate.isEmpty()) {

update accountsToUpdate;

Best Practices:

Ensure bulk handling by updating all Parent records at once.


Use a helper class or utility method for complex aggregation logic.

5.Grant parent updte trigger - https://www.youtube.com/watch?


v=RFZE42k5f9E&pp=ygUMYm9zcyB0cmlnZ2Vy
6. Grant child update trigger - https://lnkd.in/dxXY2Nis

7.Preventing Trigger Recursion

Scenario: Prevent triggers from executing multiple times in a recursive loop.

Solution:

1. Static Variables: Use a static variable in a helper class to track if the trigger has already
run.
2. Trigger Context Variables: Use Trigger.isBefore and Trigger.isAfter conditions to control
which actions run at each stage.
3. Custom Settings: Use custom settings to manage recursion limits for more complex
scenarios.

Example:

apex

Copy code

public class TriggerHandler {

public static Boolean isTriggerExecuted = false;

trigger AccountTrigger on Account (after update) {

if (TriggerHandler.isTriggerExecuted) {

return;

TriggerHandler.isTriggerExecuted = true;

// Trigger logic here


TriggerHandler.isTriggerExecuted = false;

Best Practices:

Avoid recursive triggers by using static variables.


Keep logic minimal to avoid unintended recursion across multiple records.

You might also like