Salesforce coding lessons for the 99%
Finally, Apex tutorials for point-and-click admins! Written by a self-taught Google engineer.
  • Beginner Tutorials
    • Apex
    • Certifications
    • Career Info
    • Technical Architect
    • Visualforce
    • Videos
  • Apex Academy
  • Success Stories
  • About Me
  • Misc
    • Mailbag
    • Challenges
    • Links
    • Login to my Org
Follow @dvdkliuor SUBSCRIBE!

The biggest secret Apex developers don’t want you to know!

April 7, 2014

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:


This guy is rich and famous… Otherwise we’re pretty similar.

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)

  1. Create a collection of values that you need to do a SOQL query against
        —> Create a set of all relevant Zip Codes
  2. Do a single SOQL query for your relevant records and add the results to a Map
        —> Do one SOQL query for all Territories with relevant Zip Codes
  3. Loop across all records in Trigger.new and match each record with its SOQL query result
        —> For every Account, search its Zip Code in your Map to find the matching Territory
  4. Apply your business logic to each record and its SOQL result
        —> For every Account and its Territory, create AccountShare records.

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!

28 Comments
Reshmi
March 2, 2015 @ 3:18 am

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;
}

Reply
    David Liu
    March 2, 2015 @ 6:42 pm

    Try posting this one to the SFDC99 forums, there’s a special guy there who’s great at debugging!

    Reply
chitral
August 19, 2014 @ 11:35 am

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

Reply
    David Liu
    August 19, 2014 @ 7:49 pm

    No need to use a secondary updateTask list, try doing it all in the Trigger.new loop!

    Reply
      Chitral
      August 19, 2014 @ 11:42 pm

      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
      }

      Reply
        David Liu
        August 19, 2014 @ 11:43 pm

        Try not using any lists at all =)

        Reply
          chitral
          August 20, 2014 @ 3:38 am

          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;
          }
          }

          Reply
            chitral
            August 20, 2014 @ 3:41 am

            i think it shud be
            if( oldCreatedById ! = newCreatedById )
            {
            t.ownerid=oldCreatedById;
            }

            Reply
              chitral
              August 27, 2014 @ 12:56 am

              @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;

              }
              }
              }

              Reply
                David Liu
                August 27, 2014 @ 10:52 pm

                Epic job Chitral, CONGRATULATIONS!!!!!

                Reply
kaveri
July 22, 2014 @ 10:23 am

Hi david
Looking forward for ur next chapters …..
Thank you

Reply
Viru
June 26, 2014 @ 3:09 am

Hy ! Waiting for next David (-;

Viru (-;

Reply
Steven
June 3, 2014 @ 7:23 pm

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?

Reply
    David Liu
    June 3, 2014 @ 8:12 pm

    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!

    Reply
      Steven
      June 4, 2014 @ 8:37 am

      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.

      Reply
        David Liu
        June 4, 2014 @ 8:57 am

        My pleasure!

        Reply
Chad
June 1, 2014 @ 4:22 pm

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;
}
}

Reply
    David Liu
    June 1, 2014 @ 7:32 pm

    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

    Reply
      Chad
      June 2, 2014 @ 7:53 am

      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?

      Reply
        David Liu
        June 2, 2014 @ 8:02 pm

        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

        Reply
Zak
May 23, 2014 @ 4:45 am

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?

Reply
    David Liu
    May 24, 2014 @ 12:47 am

    That’s the plan for chapter 8!

    Reply
Zed
May 21, 2014 @ 7:25 am

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?

Reply
    David Liu
    May 22, 2014 @ 5:45 pm

    That actually looks perfect!!!! It works too right? =)

    Give yourself a pat on the back Zed!!

    Reply
Narendhar
April 18, 2014 @ 2:11 am

Great Explanation…!I like this site, keep it up the good work.

Reply
    David Liu
    April 18, 2014 @ 11:57 pm

    Why thank you Narendhar ^_^

    Reply
      Ranky
      July 3, 2015 @ 11:37 pm

      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!

      Reply
Ryan Golembiewski
April 9, 2014 @ 6:49 am

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.

Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *


*

*

Theme: Simple Style by Fimply