Sunday, 12 July 2020

Episode 3-Salesforce Triggers and Scenario Based Questions

In this Blog, we will have insights about Apex Triggers and how to apply trigger concepts in real-time scenarios. This Blog is for the Beginners to Intermediate Level Developers. Read the blog till the end to know the BONUS TIP. Before wasting time let's get started.👍



The trigger is the piece of code that is executed when some specified database events occur.

Types Of Triggers:

1.Before Triggers: They are used to validate or update values of records before data is saved to the database.

2. After Triggers: They are used to access record values that are stored in the database and also when you have to make changes in other records.


Trigger Syntax:
Trigger <TriggerName> On <ObjectName> (trigger_events) {
//code 
}

You may be wondering what is Trigger Events. So ,we have 7 Trigger Events in Salesforce apex  triggers  depending upon various DML operations:

  • Before Insert
  • Before Update
  • Before Delete
  • After Insert
  • After Update
  • After Delete
  • After UnDelete

Context Variables: In order to access the records which are fired due to the Trigger, We use Context Variables in Trigger. For Example, the most commonly used Context Variables are Trigger.New and Trigger.Old.   

Trigger.New contains all the new versions of records which have caused trigger to fire.Trigger.Old contains old versions of records.

You can check out all the  Trigger Context Variables at the below link:


Check out the table to know which trigger events are allowed for 4 commonly used Trigger Context Variables.








Implementing the Trigger  Scenarios:

Before diving directly into code, identify the below points from the given requirement scenario.

1.Object on which Trigger is fired.
2.Which Trigger Events should be used and Why?
3.Objects, fields, and conditions on which DML operations need to be performed.

Scenario 1: Create an Apex trigger to update account rating (value to be Hot)  when the opportunity stage equals Closed Won.

Trigger UpdateAccountRating on Opportunity (after insert,after update){
Set<Id> accid= new Set<Id>();
if(Trigger.isAfter){
if(Trigger.isInsert || Trigger.isUpdate){
for(Opportunity o:Trigger.New){
accid.add(o.accountid);
}}}
List<Account> acclist= [select id,name from account where id in :accid];
List <Account> accountUpdatedList= new List<Account>();
for(Account a:acclist){
for(Opportunity o:Trigger.new){
if(o.stagename =='Closed Won')
{
a.rating='hot';
accountUpdatedList.add(a);
}}
}
if(accountUpdatedList.size()>0){
update accountUpdatedList;
}}

Explanation
  • As per the requirement, we are performing operations on the trigger after the user submits the record or updates the Opportunity stage value to be 'Closed Won'.Hence we have used after insert and after update trigger events.
          Trigger UpdateAccountRating on Opportunity (after insert,after update)
  • Have used Trigger.isAfter ,Trigger.isInsert,Trigger.isUpdate Context variables to control the flow of the code.
  • We have used Set to store the unique Account Ids related to opportunities ( via o.AccountId field) by iterating over the Opportunity records contained in Trigger.New.
  • Using SOQL query we have collected the list of accounts related to opportunities records which caused the trigger to fire i.e [select id, name from  account where id in :accid]
  • In the next for loops, we have iterated and checked, if the  Opportunity stage values equals 'Closed Won' then update its related account rating field value as 'Hot'.
  • Collected all the accounts meeting the specified criteria in accountUpdatedList List using accountUpdatedList.add(a).
  • In order to avoid the list being null, we have first checked whether the accountUpdatedList list size is greater than 0 then only we will update the list.
       if(accountUpdatedList.size()>0){
       update accountUpdatedList;}

I hope you have understood the trigger scenario and implementation of its code well.Let's see another trigger requirement.

Below is the optimized code for the above requirement using the map.

Trigger UpdateAccRating on Opportunity(after insert, after update){
    Set<Id> Setaccids= new Set<Id>();
    if(Trigger.isAfter){
        if(Trigger.isInsert || Trigger.isUpdate){           
            for(Opportunity opp:Trigger.new){
                Setaccids.add(opp.accountid);         
            }}}
    Map<Id,Account> mapIdvsAcc = new Map<Id,Account>([select id from Account where id in:Setaccids]);
    for(Opportunity oppobj:Trigger.new){     
        if(oppobj.StageName=='Closed Won' && oppobj.AccountId!=null){
            mapIdvsAcc.get(oppobj.accountid).rating='Hot'; 
        }
    }
    if(!mapIdvsAcc.isEmpty()){
        update mapIdvsAcc.values();
    }
}

The above code is correct but is not following the trigger best practice of using Trigger Helper class and writes all the logic in it so it can be reused later.

Let's use TriggerHelper class in the next example so that you can always use trigger best practice in your code.


 Scenario 2: Create a  Trigger that will prevent the users to create duplicate contacts email id.
i.e if you try to create the contact record with an email id that already exists then you will get an error.


trigger PreventDuplicateRecords on Contact(before insert, before update, after undelete){
List<Contact> conList= new List<Contact>();
//STEP 1
if(Trigger.isBefore && (Trigger.isUpdate || Trigger.isInsert)){
conList= Trigger.New;
}
if(Trigger.isAfter && Trigger.isUndelete){
conList=Trigger.New;
}
PreventDuplicateRecordsHandler.PreventDuplicateRecordsHandlerMethod(conList);
}

public class PreventDuplicateRecordsHandler {
    
static Set<String> oldEmailIdSet = new Set<String>();
static Set<String> newEmailIdSet = new Set<String>();

public static void  PreventDuplicateRecordsHandlerMethod(List<Contact> contactList){
//STEP 2 
for(Contact c:contactList){
if(c.Email !=null)
newEmailIdSet.add(c.email);
}
//STEP 3
List<Contact>  oldContactList =[select id,lastname,email from contact where email in : newEmailIdSet and email!=null];
//STEP 4
for(Contact c:oldContactList){
oldEmailIdSet.add(c.email);
}
//STEP 5
for(Contact c:contactList){
if(oldEmailIdSet.contains(c.email)){
c.email.addError('Duplicate Email Id is not Allowed');
}
else{
oldEmailIdSet.add(c.email);
}
}
}}

Explanation:

In this example we have used before trigger events as we have to validate record
before it gets saved in the database. Only for undelete operation After event is
used.

Step 1: Collecting the list of all new contact records in conList list variable.
Note the use of context variables in the code.
Step 2:Adding all the new email id's to the newEmailIdSet set variable.

Step 3:Querying all the contact objects to get the duplicate contact records using SOQL.

Step 4:Add all the existing duplicate email id in the Set variable oldEmailIdSet

Step 5:Iterating over the all the contact records and checking if the new contact
email id already exist then throw an error using addError Method.

In this scenario we have written all the logic in the helper class static method and
called the class method in the trigger, thus following Trigger best practice.
For Interview Questions on Triggers,Please check the below link of my blog :



Hurrah!! 😊 you have learnt everything about trigger and also learnt to implement them.

BONUS TIP: Practice is the key to implement different trigger scenarios.
Coding will not be tough if you learn,practice and master the skill of problem Solving.

Thanks for reading my Blog if you like it do comment.
You can also comment on what topic you want my next blog on.
Keep Learning and Keep Sharing your Knowledge.









15 comments:

  1. Thankyou Meenal your blog is very informative & easy to learn . Keep sharing knowledge. Above Topic Content is so good to help Learner.

    ReplyDelete
  2. Really helpful!! If some one need clarification over trigger, I will share this blog to them!! Well explained, love to read more :)

    ReplyDelete
    Replies
    1. thanks,,keep following my blog for more learning content.

      Delete
  3. Very well explained and my basics got cleared.

    ReplyDelete
    Replies
    1. Glad to Know, it is useful to you. Follow my blog to stay tuned for learning .

      Delete
  4. I simply wanted to write down a quick word to say thanks to you for those wonderful tips and hints you are showing on this site. As a result of checking through the net and meeting techniques that were not productive, Same as your blog I found another one Oracle APEX .Actually I was looking for the same information on internet for Oracle APEX and came across your blog. I am impressed by the information that you have on this blog. Thanks once more for all the details.

    ReplyDelete
  5. Thanks for sharing this blog. The content is beneficial and useful. Very informative post. Visit here to learn more about Data Warehousing companies and Data analytics Companies. I am impressed by the information that you have on this blog. Thanks once more for all the details.Visit here for Top Big Data Companies.

    ReplyDelete
  6. Hello, thanks for the information. But I would like to highlight few points:
    1. Non of your triggers are following best practices and optimum performance. Scenario 1 should use map instead of for inside for.
    2. Scenario 2 trigger is not bulkified. If we insert new contacts with same email in bulk. Your code will allow it.

    ReplyDelete
    Replies
    1. thanks for the feedback i will try to optimized my code.

      Delete

If you have any doubts ,Please let me know.

Episode 13 : RollUp Summary Trigger for Lookup Relationship

 As we all know we can use roll-up summary field logic only for the master-detail relationship, but in case you have the business requiremen...