Preface: this post is part of the Write Your First Advanced Trigger series.
Sometimes I feel like this masked dude right here because I give away industry secrets:
Coding is just like magic. The results are spectacular and it’s a great way to get dates (see above)!
But behind all magic is a trick. And once you figure out the trick, anyone can do it!
The biggest secret about Apex is that all triggers are basically the same! Learn to identify the pattern and suddenly coding is no longer secret wizardry – it’s simply a formula with different inputs.
Here’s the secret formula that every trigger follows:
(All examples from this chapter’s trigger)
Don’t believe me? This secret formula is used in Chapter 5’s trigger!
Still don’t believe me? The secret formula is also used in Chapter 5’s Quiz trigger!
Folks, every trigger follows this pattern!
Sometimes, you’ll use this pattern twice in the same trigger…
Sometimes, you’ll have a few IF statements in between…
And sometimes, your SOQL query will have another query nested inside it…
…but I guarantee you that your trigger will follow this formula!
Master this formula and you will be a master of Apex magic!
Next post: Intro to Object-Oriented Programming!
Hi David,
The following is my code for the for the trigger to “”Prevent change of ownership of open tasks when the related parent record owner is changed”” posted by Chitral, using the “The biggest secret about Apex “. But Task’s Owner is getting updated to account’s new owner. Pls let me know, whats the issue with my code..
trigger taskOwners on Account (after update) {
Set changedownerAccs = new Set();
Listupdatetask = new List();
for( Account acc :Trigger.new){
String oldOwner = Trigger.oldMap.get(acc.Id).OwnerId;
String newOwner = acc.OwnerId;
if (oldOwner != newOwner){
changedownerAccs.add(acc.Id);
}
}
List mytask = [SELECT Id,AccountId FROM Task WHERE AccountId IN:changedownerAccs];
Map accToTask = new Map();
for(Task t:mytask){
accToTask.put(t.AccountId,t);
}
for(Account a: Trigger.new){
if (Trigger.isUpdate){
System.debug(‘Entering the inner loop’);
for( Task t:a.Tasks)
{
Task tsk =accToTask.get(a.Id);
System.debug(‘Old Ownerrr>>>’+ oldOwner);
tsk.OwnerId = Trigger.oldMap.get(a.Id).OwnerId;
updatetask.add(tsk);
}
}
}
System.debug(‘updating’);
update updatetask;
}
Try posting this one to the SFDC99 forums, there’s a special guy there who’s great at debugging!
hie david ,
back again there is so much to learn thankyou for all this .I hav this trigger but m nt very sure that whether its right or not , please hav a look.
Prevent change of ownership of open taskd when the related parent record owner is changed.
trigger StopTaskOwnerChange on Task (before update)
{
List updateTask = new List();
for(Task t : trigger.new)
{
if(t.ownerId != t.CreatedById)
updateTask.add(t);
}
for(Task t : updateTask)
{
t.ownerId = t.CreatedById;
}
help =) needed
No need to use a secondary updateTask list, try doing it all in the Trigger.new loop!
coop david ! I think it shud be like * scratching head*
For( task t : trigger.new)
{
If ( t.ownerId ! = t.createdById)
UpdateTask.add(t);
}
Update updateTask; // updating in task list
}
Try not using any lists at all =)
thnku david , i vl keep fytin untill i get it ryt
trigger StopTaskOwnerChange on Task( before update )
{
for(task t : trigger.new)
{
string oldCreatedById = trigger.oldMap.get(t.AccountId).CreatedById;
string newCreatedById = t.CreatedById;
if( oldCreatedById ! = newCreatedById )
{
t.ownerid=t.CreatedById;
}
}
i think it shud be
if( oldCreatedById ! = newCreatedById )
{
t.ownerid=oldCreatedById;
}
@david
happy ! i got it finaaly
thnkuuu fr ur epic lessons
trigger preventTaskOwnerChange on Account ( after update)
{
list updatetask = new list();
for(account a :trigger.new)
{
string oldownerid = trigger.oldmap.get(a.id).OwnerId;
string newownerid = a.ownerid;
if(oldownerid != newownerid)
{
for( account acc : [ select OwnerId, ( select OwnerId from tasks ) from account])
{
for( task tsk :acc.tasks)
{
tsk.ownerId = oldownerid;
updatetask.add(tsk);
}
}
update updatetask;
}
}
}
Epic job Chitral, CONGRATULATIONS!!!!!
Hi david
Looking forward for ur next chapters …..
Thank you
Hy ! Waiting for next David (-;
Viru (-;
Hi David,
With your help and your site, I have written and deployed several Apex triggers which all work great. Thanks!
One of the triggers prohibits users from changing event fields if the event was created by a particular custom object. This prohibitUpdate trigger is a before update and never uses any update or insert dml it only changes the fields back to their original values and lets the system handle the database changes. Once the user clicks save they are taken back to the calendar. Problem is that I want to notify the user that they cannot make changes to these event from the calendar, only from the custom object.
Any suggestions on how to approach it?
Great job Steven!
Look into preventing this action using validation rules instead of code. For example, you can have a validation rule that prevents changes to a record if a certain code populated field is not blank!
I had looked at the validation rules prior to developing the trigger, but, I guess, got confused thinking that the ‘old’ values weren’t available to overwrite the ‘new.’ Didn’t think about just rejecting all with a simple Boolean variable. This is much easier and straight forward. Thanks again.
My pleasure!
Hello David,
I really look forward to the other lessons. I have really enjoyed the current information and I feel a bit informed on how to code apex triggers. speaking of apex triggers, I have a trigger i’m writing on a parent object to talk to the child object to fire a workflow rule that is on the child object. the problem is I keep getting an error stating the column is not in the child object. could you please take a look at the code and tell me what you think. the parent object is Opportunity the child object is Lead Source. What I’m trying to accomplish is have a date from the parent object go to the child object to stop an email alert from being sent.
trigger FieldVisitComplete on Opportunity (after insert, after update) {
Set oppIds = new Set();
for (Opportunity parentObj : Trigger.new)
{
oppIds.add(parentObj.Id);
}
Map parentObjList = new Map([Select Initial_Field_Visit__c FROM Lead_Source__c WHERE ID IN :oppIds]);
for (Opportunity opp : Trigger.new) {
if (opp.Initial_Field_Visit_Completed__c != null) {
Lead_Source__c lsrc = parentObjList.get(opp.Id);
lsrc.Initial_Field_Visit__c = opp.Initial_Field_Visit_Completed__c;
}
}
{
update parentObjList;
}
}
First off great start on your code, very impressive!
Second off – have you tried doing this with a simply formula field on the Lead Source object instead? Might be easier!
If for whatever reason you need, code the simple reason why your code isn’t working is because your Map isn’t what you expect it to be =) You have a Map of the Lead Source ID as the search term and the Lead Source as the search result. It looks like you actually need the search term to be of the Opportunity ID instead!
So to fix it, just loop through every record in your SOQL query and add the Opp ID as the search term to the Map instead =)
Hope this makes sense!
David
thanks David. Not sure I understand. In Map ID do i change all names to the parent object (Opportunity) from the child object (Lead_Source__c?
Keep your Map as the same type ID/Lead_Source__c however instead of populating it by doing a SOQL query in the new Map(), do this:
for (Lead_Source__c ls : mySoqlQueryResults) {
parentObjList.put(ls.Opportunity__c, ls); // Make sure to query for the opp ID used in your SOQL
}
Hope this helps =)
David
David,
Do you have any plans of farming the code into a class and having the trigger call the methods rather than having the code all in the trigger body?
Would be awesome to have a little chapter about taking the trigger.new() and passing it into a class as an attribute/parameter?
That’s the plan for chapter 8!
I finally (after about 3 billion hours) have written a trigger that simply takes a value from a contact record, and updates a field on the account record with the same value.
I needed a bit of help from a colleague to get it through all the debugging, but here it be:
trigger Update_interests on Contact (After Update, After Insert) {
Map accountMap;
Set accountIds = new Set();
List accountsToUpdate = new List();
for(Contact c : trigger.new)
{
accountIds.add(c.AccountId);
}
accountMap = new Map([SELECT ID FROM Account WHERE Id IN :accountIds]);
for(Contact c : trigger.new)
{
Account a = accountMap.get(c.AccountId);
a.interests__c = c.Interests__c;
accountsToUpdate.add(a);
}
if(!accountsToUpdate.IsEmpty())
{
update accountsToUpdate;
}
}
What would you change?
That actually looks perfect!!!! It works too right? =)
Give yourself a pat on the back Zed!!
Great Explanation…!I like this site, keep it up the good work.
Why thank you Narendhar ^_^
Even a Kindergarten Kid can learn coding if he sees http://www.sfdc99.com. Thanks a lot David for your down to earth explanation of things. You should be proud of yourself mate!
I’ve been working in patterns with my 3 year old son and I actually said, “I bet APEX/Triggers are some type of pattern”, why can’t it be just like the kids show: Team UmiZoomi? David, this is a great post. Thank you for laying this groundwork for us.