Preface: this post is part of the Advanced Apex Concepts series.
Let me give you the definitive answer to perhaps the most commonly asked Apex question:
“Should I use a before or after trigger?”
95% of triggers are before triggers – so if you’re unsure, go with before!
You may be wondering why so many triggers are before triggers. There’s a good reason – they’re simpler to use. If you need to make any changes to a record entering your after trigger, you have to do a DML statement. This isn’t necessary in a before trigger – changes to records entering your trigger always save!
The specific use case of an after trigger is when you need to associate any record to a record being created in your trigger. Here’s an example:
// Automatically create an Opp when an Account is createdtrigger AutoOpp on Account(after insert) {List<Opportunity> newOpps = new List<Opportunity>(); for (Account acc : Trigger.new) { Opportunity opp = new Opportunity(); opp.Name = acc.Name + ' Opportunity'; opp.StageName = 'Prospecting'; opp.CloseDate = Date.today() + 90;opp.AccountId = acc.Id; // Use the trigger record's IDnewOpps.add(opp); } insert newOpps; }
Moral of the story – before triggers are kings of the jungle! You could say they are “apex” predators in Salesforce, ha ha ha..
Next post: Debug your code with System.debug!
Hi David,
I’m following your Apex Academy: Absolute Beginner’s Guide to Coding in Salesforce an there you taught about HelloWorld Trigger where we are using after update but why it is invoked while creating a record (Cuz before Insert is needed for it) . So why after update is being invoked when I create a record.(I’ve used a freshly created playground for it ).
Thank You
Sir I have a query in which case we need to use after insert and after update trigger.This is confusing when to use after Insert and after update trigger.Can u help me to figure it out
When I set an Opportunity to complete and that same opportunity meets certain conditions, I need to create a clone of that opportunity with a different opportunity record type. Before or After trigger? Or both?
Try before first!
Hi David,
If there is field update on the other records related to the record being updated then in this case it should be before or after ?
Best Regards,
Krishna Bidwai
After Trigger
Hi David Liu,
Thanks for giving the clear picture of When to use before vs after triggers.
My doubt has vanished now. Finally, I am able to write Apex Trigger.
Thanks for sharing this article.
Excellent! Congrats!!
Hi david,
I have a requirement,can you please help me how to resolve the task,can it be resolved by using the trigger or can we use the process builder? Here is my requirement below
Assume that i have Amount field on Account and as well as on Contact.if i change the amount on account,all the child should update with the equal amount and all the total amount should be equal to account amount.
for ex:if account(amount_field)=100,i have 10 childs contacts for account,each child contact should have amount_field value as 10 so total should be equal to account(amount_field).
each child
trigger splitTotalAmount on Account (after update) {
Map myMap = trigger.newmap;
List lstc = [select id,name,accountid,Amount__c from contact where accountId in:mymap.keyset()];
List cc = new List();
for(contact c:lstc){
c.Amount__c = myMap.get(c.AccountId).Total_Amount__c/lstc.size();
cc.add(c);
}
update cc;
}
Great!
Hi David,
I wrote trigger as following:
trigger UpdateSADeclined on Task (before insert,before update) {
for(Task x: Trigger.new)
{
If (x.Declined_by__c==null|| x.Date_Time_Declined__c==null|| x.Declined_Reason__c==null)
{
x.SA_Declined__c=true;
}
else
{ x.SA_Declined__c=false;
}
}
}
By this trigger checkbox field SADeclined is checked whenever any of date field value ( declined by, date time declined and declined reason) is null, otherwise it’s unchecked ( means whenever all three fields are populated).
Now, My requirement is whenever there is value in date field SADeclined should be unchecked, i.e. when all three date fields are null the SADeclined will be checked.
Kindly help me solve this.
Change 1 line of your code to this :
If (x.Declined_by__c==null && x.Date_Time_Declined__c==null && x.Declined_Reason__c==null)
Basically replace OR operator with AND.
Hi David,
I am working in SFDC. I have two certs. ( ADM201 and platform app builder). As I am mechanical engg., I have little coding knowledge. sfdc99 is really helping me to learn the basic coding. Great work!!!
Thank you Dilip!
Hi David,
To introduce my self I am Arun. Your website SFDC99 is really superb. All of your articles are great articles and inspiring many people like me.
I have a question regarding triggers. I am going through Apex Design patterns “Bulk State Transition” pattern which is related to triggers. In the example given why the trigger is after trigger(“after insert, after update) instead before trigger. I do not see any ID that is being used in the example given. Can you please clarify..? Would appreciate your help.
here is the link for your reference https://developer.salesforce.com/page/Apex_Design_Patterns
Please look for the last pattern
Thanks
Arun
Thanks Arun!
The code in line 17 very likely will use the opp ID, since it needs to associate the order with the opp!
17: new OrderClass().CreateOrderFromOpptys(closedOpptyList);
Cheers =)
David
Got it. Thank you for the clarification !!
Hi David,
I am trying to check if Lead to insert is duplicate and if it is then store it in separate custom object(Duplicate_Lead__c). but in query it is returning the record i am inserting i.e. for every lead i insert this trigger is matching its fields with itself and creating duplicate record in custom object.
How to avoid this?
trigger LeadDuplicateTrigger on Lead(after insert) {
List Exleads = [Select id, name, email, MobilePhone, Program__c From Lead];
List DupLeads = new List ();
for(Lead l : Trigger.new){
for(Lead Ele : Exleads){
if(( (l.email == Ele.email) && (l.Program__c==Ele.Program__c) ) || ( (l.MobilePhone == ELe.MobilePhone) && (l.Program__c==Ele.Program__c) )){
Duplicate_Lead__c DPLead = new Duplicate_Lead__c();
DPLead.Name = ‘Duplicate Of’+’ ‘+ Ele.Name;
DPLead.Admission_Stage__c = l.Admission_Stage__c;
DPLead.Admission_Status__c = l.Admission_Status__c;
DPLead.Agency__c = l.Agency__c;
DPLead.Initial_Owner__c = l.Initial_Owner__c;
DPLead.Initial_program__c = l.Initial_program__c;
DPLead.Program__c = l.Program__c;
DPLead.Lead_State__c = l.Lead_State__c;
DPLead.Test_Score__c = l.Test_Score__c;
DupLeads.add(DPLead);
}
}
}
Insert DupLeads;
Hi,
I have managed to get required output. But just want to check is there anything i am doing wrong..Any other better way of achieving the same:
[ Requirement : there is a Lead record 1 (email=abc@gmail & program__c=ABC) exist in system….then i insert Lead record 2 (email=abc@gmail & program=ABC) now field values of Lead record 2 are copied in custom object (Duplicate_Lead__c) and then remove record 2. ]
trigger LeadDuplicateTrigger on Lead(after insert) {
List Exleads = [Select id, name, email, MobilePhone, Program__c From Lead WHERE Id NOT IN : Trigger.new ];
List DupLeads = new List ();
for(Lead l : Trigger.new){
for(Lead Ele : Exleads){
if(( (l.email == Ele.email) && (l.Program__c==Ele.Program__c) ) || ( (l.MobilePhone == ELe.MobilePhone) && (l.Program__c==Ele.Program__c) )){
Duplicate_Lead__c DPLead = new Duplicate_Lead__c();
DPLead.Name = ‘Duplicate Of’+’ ‘+ Ele.Name;
DPLead.Admission_Stage__c = l.Admission_Stage__c;
DPLead.Admission_Status__c = l.Admission_Status__c;
DPLead.Agency__c = l.Agency__c;
DPLead.Initial_Owner__c = l.Initial_Owner__c;
DPLead.Initial_program__c = l.Initial_program__c;
DPLead.Program__c = l.Program__c;
DPLead.Lead_State__c = l.Lead_State__c;
DPLead.Test_Score__c = l.Test_Score__c;
Lead updLead=[select id from Lead where id = :Ele.id];
updLead.Duplicacy__c = true;
Update updLead;
Lead DelLead=[select id from Lead where id = :l.id];
Delete DelLead;
DupLeads.add(DPLead);
}
}
}
Insert DupLeads;
}
I read so much documentation on this and laughed so hard after looking at the graphic which is absolutely accurate and simple to understand. Before seeing this I was getting “Read-only” errors and so forth. Thanks so much!
bro the joke was very bad.need to improve humor skills.
LOL!
This graphic is COMPLETELY WRONG.
Salesforce’s own documentation on Triggers (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers.htm) states:
“There are two types of triggers:
– Before triggers are used to update or validate record values before they’re saved to the database.
– After triggers are used to access field values that are set by the system (such as a record’s Id or LastModifiedDate field), and to affect changes in other records, such as logging into an audit table or firing asynchronous events with a queue. The records that fire the after trigger are read-only.”
Salesforce and I agree Monsieur =)
The graphic is just a simplification of the text from the Salesforce documentation, which is a very confusing read!
David
Thanq Good Code
Hi David,
I have written your trigger in the below form – I am trying to bulkify the code! would that be correct too in this instance?
// Create an opp when an Account is created
trigger CreateOppWhenAccountCreated on Account (after insert) {
List newOpportunities = new List();
for (Account account : trigger.new) {
Opportunity opp = new Opportunity(Name=account.Name + ‘ opportunity’,
StageName=’Prospect’, CloseDate=Date.today() + 90,
AccountId=account.Id);
newOpportunities.add(opp);
}
insert newOpportunities;
} // end trigger
Thank you, Sinan
You got it, great job Sinan!!
Hi David, first of all thank you so much.I got my DEV 401 and ADM201, just started apex programming and your lessons are so helpful.Now i am working on triggers and my question is “why can’t we create triggers in setup at Develop/apex triggers(because we don’t have new button, so we have to use developer console to create triggers)”
You’re right! For the Apex Class you can…. for the Triggers you cannot! I think it’s a choice of Salesforce. Maybe because they think the Triggers could be more harmful!
Hey rushi, you can create triggers from Setup, not from Develop>Apex Triggers but from the Object itself. For standard objects (like Account), Account>Triggers>New. For Custom Objects, Custom Object>Triggers>New.
Great site David….It realy helps us..
Hi David,
Thanks for the Post.
I have understood the post shared by you, but I am still not confident accepting it while considering the concept of Apex Transaction Roll Back. Please share your thoughts on it.
Consider the scenario where I need to create object B on update of object A. Typically, I should create a ‘before update’ Trigger that calls a class which creates a record in object B.
Per the ‘Apex Transaction’ concept, if there is a failure outside the transaction boundary like due to Custom Validation Rules (that executes after Before Triggers and before Record Save), a roll back will not happen. Because in this scenario the transaction boundary will be the Trigger (including any class that it invokes).
This would lead to inconsistency in the system – Object B record created while Object A record still not saved with the updates.
Am I getting all this correctly?
Thanks for your help,
AR
If you want to create a record in object b after updating a record in object a, then you should use AFTER update trigger and not BEFORE trigger.
Hi David,
Good session from you. Below is my understanding out of ur session.
1. Before trigger, if at all the triggered records had to follow some complex business rules / go with some modifications before it can be saved. Its a before save scenario.
2. After trigger, means a record is already saved to DB, what next? insert / update dependent objects, fire future calls (may be for some webservice calls / api for sync up with different systems etc) and so on.
Thanks,
Santhi C
Yes you’re getting the hang of this!
I got more understanding by your explanation. Thanks :)
HELP me through this . Task is to implement a trigger on contact This trigger should check that
1.related account must not have more than 10 childs.
2.Update child count in account through trigger whenever a contact is created or update or deleted.
3.While creating a new record in contact it should not allow duplicate record which have email and mobilenumber.
Hi david you are doing good job keep it up!!!
trigger AutoOpp on Account(after insert) {
List newOpps = new List();
for (Account acc : Trigger.new) {
Opportunity opp = new Opportunity();
opp.Name = acc.Name + ‘ Opportunity’;
opp.StageName = ‘Prospecting’;
opp.CloseDate = Date.today() + 90;
opp.AccountId = acc.Id; // Use the trigger record’s ID
newOpps.add(opp);
}
insert newOpps;
}
am getting error like Loop variable must be Sobject type
@muni
its working fine
just copy paste this code.
trigger AutoOpp on Account(after insert)
{
list newOpp = new list();
for(Account acc: trigger.new)
{
opportunity opp = new opportunity();
opp.Name = acc.Name + ‘ Opportunity’;
opp.StageName = ‘Prospecting’;
opp.CloseDate = Date.today() + 90;
opp.AccountId = acc.Id; // Use the trigger record’s ID
newOpp.add(opp);
}
insert newOpp;
}
Check in your code
opp.Name = acc.Name + ‘ Opportunity’; ( opportunity should be single quote ‘opportunity’).
opp.StageName = ‘Prospecting’; (same here).
Add this code line for list declaration.
List newOpps = new List();
List newOpps = new List();
list new_opp = new list();
try defining the object that your List should be holding e.g.
List newOpps = new List();
Hello sir…
You are declareda as “list” fine but what type of list that is? So declare ad
List newpp= new list.
Not sure what the question is sorry! Can you provide more detail?
Hi,
Your error in Line#2. replace it to List newOpps = new List();
Enjoy (Y)
after line1 u should write
if(Trigger.isInsert){
Hi David,
I am new to Apex programming. I wrote one trigger. I dont know whether it is right or wrong pls guide me. my code is below
Problem:
1) when an account is set to active, the address on the account should be copied as a primary address on the related list
2) if there is a duplicate, dont insert new address, but make the existing one primary
3) if user changes the address on the account screen, insert a new address on the related list and make this new as a primary (dont delete the old primary address)
I have 3 things to do in one trigger.
Code:
trigger updateprimaryaddress on Account (after update,after insert) {
// list updateToaddress = new list();
list changedAccount = new list();
setids = Trigger.newMap.keyset();
if(trigger.isupdate){
for(Account a : trigger.new){
if(a.status__c == ‘Active’) {
list newacctsaddr = new list();
newacctsaddr = [select id, address__c,city__c,country__c from account where id in :trigger.New];
if(a.address__c != trigger.oldMap.get(a.id).address__c||
a.city__c != trigger.oldMap.get(a.id).city__c||
a.country__c != trigger.oldMap.get(a.id).country__c) {
newacctsaddr.add(a);
}
} else if(trigger.isInsert){
list updateToaddress = new list();
updateToaddress = [select account__c,address__c,name__c,city__c,country__c from address__c where id in :ids];
for(address__c ad : updateToaddress) {
ad.name = a.country__c;
ad.account__c = a.name;
ad.address__c = a.address__c;
ad.city__c = a.city__c;
ad.country__c = a.country__c;
ad.primary_address__c= True;
ad.IFS_linked__c = True;
updateToaddress.add(ad);
}
if(!updateToaddress.isEmpty()){
update updateToaddress;
}
}
}
}
}
But it is not working for me. If i create one account with address information when status is made active, it is going to address related list.
pls help.
hi david ,
m stuck here
in this vf page the quick access menu display arrow button initialyy, (when we click the arrow button the quick acces menu opens) similar to salesforce quick acces menu …
i want that instead of arrow it shud be wriiten FAQ (vertically) like this
F
A
Q
aroow button may/maynot be present but i require that FAQ to display .
//code
//arrow button ..here how do i add FAQ vertically
//code
i cudnt post my code here
Hi David
I came across these words Recursive Trigger and Circular Triggers ….. Can u explain me those things and where do we use them
This is recursion!
Pretty rare that you’ll ever need to use it!
Excellent site really an thankful to you
Hey David !
First of all thanks, you are doing such a great job !
after this post i know which time i have to use after trigger and which time i have to use before trigger but only the time when i create a record i mean at ” insert ” time.
I am confused for the Update event. I mean in inserting time i know if i need record id then i have to use after trigger but in update, (i think ) we have record id in both event before and after so what we have to use?
Great question!
A lot of triggers are after insert, before update. Both don’t have to be insert and I recommend using after insert, before update as well!
Hi guys Nice Explanation..
Thanks for your valuable discussions.
trigger AutoOpp on Account(after insert) { List newOpps = new List();
for (Account acc : Trigger.new) {
Opportunity opp = new Opportunity();
opp.Name = acc.Name + ‘ Opportunity’;
opp.StageName = ‘Prospecting’;
opp.CloseDate = Date.today() + 90; opp.AccountId = acc.Id; // Use the trigger record’s ID newOpps.add(opp);
}
insert newOpps;
}
Error: Compile Error: Variable does not exist: Name at line 4 column 23
I m getting this why i m getting this error.?
please give me some examples to write the effective trigger
Saves 100% in my org!
Add ListnewOpps=new List(); before the for loop
hi santosh,
opp.Name = acc.Name + ‘ Opportunity’;//reason is single code problem in ‘opportunity’
don’t copy this line particularly in your trigger page..just type and execute you got a solution
ALL THE BEST
thanks
sasidhar
Hi David,
First of all a BIG THANK YOU. Your efforts for making this site is clearing my many and more concepts and improvising my coding style for writing more efficient, scalable code. First time, I am feeling like I am learning code. THANK YOU VERY MUCH.
I have some below questions on this topic:
1) In the above diagram, in one of decision box, you have stated ‘Does the trigger run on insert’. Here, ‘Insert’ means on click of ‘Save’ button/Insert of records through tools like Data Loader etc? Am I considering it correct?
2) Why we need to use DML Statement in ‘after trigger’ and not in ‘before trigger’?
3) First reason of using after trigger is ‘when you need to associate any record to a record being created in your trigger’.
Can you explain some more scenarios(from your daily coding experience) where we should use ‘after trigger’ ?
Thank you,
Amit
wOOt wOOt!
“on insert” means any time a record is created, whether it’s created through Data Loader or a Save button!
DML is needed in after triggers because after insert/update literally means “after the DML happens”. So before insert/update means “before the DML happens”. The DML I’m referring to is the implicit DML happening when you hit the “Save” button!
After triggers are only needed when you need the ID of the records being created. Most of the time you’ll need these IDs only if you’re relating completely new or different records to it in a Lookup or Master-Detail field. For example, if I want to relate Task X to every new Contact being created, I’d need the Contact ID to populate in a Task field.
Hope this helps! Check out the quiz for that Chapter for more examples!
David
I had a use case today where an after trigger was required. I was updating related child records when certain fields on the parent were updated. Regular text fields were updated on the child record no problem, but the picklist fields were acting funny. Once I changed the trigger to an after trigger, the picklist fields were updated correctly.
Great use case! There actually are rare cases where the diagram above is too simple to make this call! I will talk about this in a future post!
David,
I’m struggling with a trigger. I was thinking about whether doing an after insert would help but I think the issue is in the test class, rather than the trigger.
Here’s the scenario: when creating an event of type “New Member Briefing” I want the custom date field “Member Announcement Date” on the client’s contact record to be copied into the same field on the event record so I can do a report on the number of days between “new member announcement” and the date of the event (I have a UsableDueDate field and trigger set up to allow me to do this).
Below is my trigger so far. It’s based on somewhat a similar scenario I found online. I may have got confused because in the example I cribbed from the date field was going from child to parent not parent to child. It’s my first one, so please bear with me.
trigger NMBDateTrigger on Event (after insert) {
List EventIDs = new List();
List updateNmbEvents = new List();
for (Event nmbEvent : trigger.new) {
EventIDs.add(nmbEvent.Id);
}
Map ContactUpdateMap = new Map();
for (Contact newmember : [Select ID, member_announcement_date__c FROM Contact WHERE ID in:EventIDs])
{
ContactUpdateMap.put(newmember.ID, newmember);
}
for (Event nmbEvent : trigger.new) {
Contact updateNmbEvent = ContactUpdateMap.get(nmbEvent.Id);
if (nmbEvent != null) {
if (nmbEvent.Type == ‘New Member Briefing’) {
updateNmbEvent.Member_Announcement_Date__c = nmbEvent.Member_Announcement_date__c;
update updateNmbEvents;
}
}
}
}
END OF TRIGGER
Here’s the test class so far.
@isTest
public class TestNMBDateTrigger{
static testMethod void testNmbDate() {
//create records to test
Contact c = new Contact();
c.FirstName =’John’;
c.LastName = ‘Smith’;
c.Member_Announcement_Date__c = System.Today();
insert c;
//create NMB event
Event nmbEvent = new Event();
nmbEvent.Subject = ‘Test NMB Event’;
nmbEvent.Type = ‘New Member Briefing’;
// David, do I need the ID for the contact first?
// If the test contact and event are created at the same time, how do I get this? I wouldn’t expect this to work, as it is.
nmbEvent.WhoId = ‘c.Id’;
try{
insert nmbEvent;
} catch (Exception e) {
System.debug(‘An error happened, as predicted!’);
}
List nmbEvents = [SELECT Id FROM Event WHERE (Member_Announcement_Date__c = TODAY)];
System.assertEquals(1,nmbEvents.size());
}
}
END OF TEST CLASS
I tried to run the code but got an error saying, “System.StringException: Invalid id: c.Id”, which makes sense since the event can’t get the contact ID for the WhoID until after the contact record is created. In real life, this isn’t an issue since the contact is there already but I can’t figure out how to test this. I’m extrapolating from an example test class, so I don’t think it’s finished yet.
Thanks for any help you can provide!
Ian
Hey Ian,
Better to start this trigger from scratch honestly =) It will be more intuitive and easier to change in the future.
Here are the steps you need:
1. Use a before trigger – after triggers are almost never used:
https://www.sfdc99.com/2014/01/25/use-vs-triggers/
2. Create a set of all WhoIds on all events
– Note to only add contacts who IDs, not leads! You can tell by the first three characters of the ID
3. Do a SOQL query for all Contacts in step 2, making sure the query the contact’s date field:
https://www.sfdc99.com/2014/01/20/soql-queries-inside-loops/
4. Add all contacts in step 3 into a Map that points to the date field:
https://www.sfdc99.com/2014/01/25/use-maps-navigate-across-lists/
5. For each event, search the map for the right contact ID then populate the date field =)
Highly recommend doing this process – it’s the template that most triggers follow and will serve you well in the future. Chapter 5 is entirely dedicated to this template =)
David
Thanks for the guide! I’ll let you know how it goes.
..I am following your lessons .great explanations.Thank you for this wonderfull site
Upagna! You have an awesome name!
Great David !
It’s nice to read the best practices with examples at one place. I am waiting your posts on integration with third party api.
Congrats on being MVP.
—–
ravi
Thank you Ravi – integrations will come soon!
No words you really Awesome..I am completely following your lessons what a great explanations..waiting for next lessons..Thank you.. Any ways congrats for MVP..
My pleasure Mani =) Tell your friends!