How to write a basic APEX Trigger


Does this picture have anything directly to do with an APEX Trigger? Of course not. However, it does accurately depict the frustration I experienced the absolute first time I tried to write a Trigger.  (Bonus points if you have seen Bloodsport…)

The purpose of this article is to provide a declarative Admin (a.k.a “ButtonClickAdmin“) a decent primer to writing their first Trigger.  I do need to however go through the “declarative before programmatic” tenet – meaning that you always try to make something work through the Salesforce UI before going to a code / programmatic route.  A lot of triggers I’ve seen out there were written before a lot of the new cool features of Salesforce have been released – keep in mind these things:

  • Formulas
    • Salesforce provides you a rich relational pathing ability to grab fields of a Parent or Lookup simply by building it through a formula
  • Workflows
    • You can do Field Updates, Tasks, Emails, and Outbound Messages using Workflow
    • As of Spring ’12, you can now do “cross-object” Field Updates – before Spring ’12 a LOT of Triggers I saw existed just to do this (page 152 of the Spring ’12 Release Notes)
  • Validation Rules
    • Apart from the cross-object stuff, this is the other area where a lot of Triggers are written
      • Which can be OK if you have CRAZY complex computations to perform that you just can’t do with a V-Rule formula…
    • Use V-Rules to do your traditional data validation on fields

Salesforce does have some great documentation to get you started:

I’m going to pick and pull apart the documentation to give you an example of how I wrote a trigger just a while back to automatically add a Chatter post to a parent record when a child record was created.  You could really take this example and apply it to creating a record in any business case however.

First however, let’s dive into how a Trigger is structured…

Structure of a Trigger

trigger AddChatterPostToParent on ChildObject__c (after insert, after update) {

This is defining your trigger.  It’s stating that this is:

  1. a Trigger.
  2. “AddChatterPostToParent” is the name of this trigger
  3. “ChildObject__c” is the object that this trigger will be firing against
  4. The database operations that this trigger will fire on – your choices are “insert, update, delete, and undelete”
    • “before” = you have the ability to CHANGE values of fields
    • “after” = you do NOT have the ability to change values of fields
    • Great post from Salesforce on the pros vs. cons on before/after HERE

When I’m typically writing triggers, I like to put some comments at the beginning to describe what I’m trying to accomplish:

trigger AddChatterPostToParent on ChildObject__c (after insert, after update) {

//////////////////////////////////////
// This trigger creates a Chatter Feed item for the parent record
// when a child record is created
// Created 20120718 Andy Boettcher
// Modified 20120718
//////////////////////////////////////

/* TRIGGER LOGIC WILL GO HERE */

}

Resources Available to You

When writing a trigger, you have a few objects already ready for you to use:

Collections

  • “trigger.new” – this is a collection of the new records you are working with.
  • “trigger.newMap” – this is an indexed collection (map) of the new records you are working with.
  • “trigger.old” – this is a collection of the OLD values of the records you are working with, such as in the case of an update.
  • “trigger.oldMap” – this is an indexed collection (map) of the old records you are working with.

True/False Variables

  • “What kind of action am I working with?”
    • Trigger.isInsert
    • Trigger.isUpdate
    • Trigger.isDelete
    • Trigger.isUnDelete
  • “Is this a before or after action?”
    • Trigger.isBefore
    • Trigger.isAfter

// Run this code if we’re dealing with an Insert only
if(Trigger.isInsert) {
blah blah blah
}

// Run this code if we’re dealing with an Update only
if(Trigger.isUpdate) {
blah blah blah
}

// Run this code if we’re dealing with either an Insert or Update only
if(Trigger.isInsert || Trigger.isUpdate) {
blah blah blah
}

** GOTCHA ALERT! **

Here’s one of the biggest “gotchas” you’ll encounter working with APEX and Triggers – you are already definitely aware that Salesforce has “limits” on just about everything; Limits on objects, fields, data size, etc.  The same goes for APEX, but you have to account for it yourself in your code vs. having Salesforce nicely remind you when you’re near the threshold.  The official term is “bulkification” – meaning that our code has to be able to handle anywhere from a single record up to thousands without hitting a limit.  I will absolutely show and describe how we are dealing with this in the examples that follow.

Trigger Logic

The best way I can suggest bridging the concept of writing a formula / workflow in Salesforce and writing a Trigger is to “speak” what you want to do.  As you’re writing the Trigger, it’s very easy to get stuck on a tangent and over-code your solution.  Speak it, write it down, and then go through your code when you’re done to make sure you didn’t get off track.

  • “On all records coming through my trigger, I want to create a Chatter Post on the parent object.” (this is our example)
  • “On only the records with a value of “Tuesday” on the “Day Of Week” field, I want to create a new record in the “X” object.”

Putting the Trigger Together

trigger AddChatterPostToParent on ChildObject__c (after insert, after update) {

//////////////////////////////////////
// This trigger creates a Chatter Feed item for the parent record
// when a child record is created
// Created 20120718 Andy Boettcher
// Modified 20120718
//////////////////////////////////////

// Create Empty List to hold new items (bulkification)
List<FeedItem> lstFeeds = new List<FeedItem>();

// Loop through Comments and create Feeds
for(ChildObject__c c : trigger.new) {

FeedItem fi = new FeedItem();
fi.Type = ‘TextPost’;
fi.ParentId = c.ParentField__c;
fi.Body = ((Trigger.isInsert) ? ‘created’ : ‘updated’) + ‘ a Child Object on this Parent.’;

lstFeeds.add(fi);

}

// Insert List if there are records contained within
if(lstFeeds.size() > 0) { insert lstFeeds; }

}

Those Pesky Unit Tests

With any APEX code, you need to write a Unit Test before you can deploy this new trigger to a Production environment.  Your Unit Test is meant to basically emulate how your code would be executed, and is structured to test if your desired result is what happens to come out of it.

To create a Unit Test for this Trigger, we need to create a Class that creates a Parent Object first, then creates a Child Object related to the Parent.  To be 100% kosher, we need to then update the new Child Object to cover both sides of the “if” condition for the Chatter Post body.

@isTest
private class AddChatterPostToParent_UnitTest {

static testMethod void testTrigger() {

// Create Parent Object
ParentObject__c p = new ParentObject__c();
p.Name = ‘Test Parent’;

insert p;

// Tell Salesforce that we are starting our test now
test.startTest();

ChildObject__c c = new ChildObject__c();

// Relate this Child to its Parent from the inserted record above
c.ParentId = p.Id;

c.Name = ‘Test Child’;

// Insert Child Record
insert c;

// Update Child Record
update c;

// Tell Salesforce we’re done with our test
test.stopTest();

}
}

Writing your first Trigger is kind of like jumping into the deep end of the pool with a lead weight belt, but that’s what your Sandbox is for.  Tinker, learn, ask questions…leverage the community.  You’ll get it.  =)

Advertisements

18 thoughts on “How to write a basic APEX Trigger

  1. Pingback: Force.com Design Patterns: Fundamentals of Writing Apex Triggers | Delivered Innovation Blog

  2. I am attending an intro developer course in 2 weeks and this neatly explained trigger structure in a way that is clear to new and experienced admins. Thank you!

  3. Could we do something like this to possibly “phone home and log data” before a user deletes a post / comment / message etcetra? Maybe phone home to a server for logging deletes, or adding removed comments to a new table perhaps?

    • Hey Jd!

      Yes, but not directly. You cannot make a “callout” directly from a trigger, but you can invoke some Future Apex with that callout in it to do what you’re looking for.

      -Andy

  4. trigger account_email on Account (before delete, after update)
    {
    for(Account NewAcc : Trigger.new)
    {
    Account Old= Trigger.oldMap.get(NewAcc.ID);
    if(NewAcc.Name!=Old.Name)
    {
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    mail.setToAddresses(new String[] {‘khubeb@sv-sol.com’});
    mail.setSubject(‘Testing Trigger’);
    mail.setPlainTextBody(‘This is a test for trigger’);
    mail.saveAsActivity = false;
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }

    }
    }

    Sir i m new in creating a trigger in salesforce i want to create a trigger on account object i want to send a notification mail to me if any user changes his or her name in an organization

  5. I liked the way trigger is explained. Could you also please send me some link that has more than 100 example triggers and use cases for it. I am new to sfdc and would like to go through these triggers and get a better grip of it.

    Thanks
    Amit

    • Amit,

      What do you mean “more than 100 example triggers”? Do you mean you are looking for other examples of how SFDC developers create and use triggers, or that you want to see some that deal with more than 100 records?

      -Andy

  6. Hi there,

    I’m trying to launch an approval process with a trigger.

    In this project I’m working on, ALL leads have to be approved the moment they are created. Everything is set up perfectly with record types, page layouts etc etc. BUT a lot of the sales team forget to press the send for approval button. Hence the need for me to create a trigger.

    I am very new to programming and Apex is the first one I’ve started learning.

    This seems like a simple thing to programme, but I just can’t seem to get it right.

    So far I’ve got this

    rigger ApproveLead on Lead (before insert) {

    if(Trigger.isinsert) {

    Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();

    }}

    But the approval process isn’t working 😦 Any ideas?

  7. Hi Andy, How do I write a trigger to update a Leads custom field (e.g. Lead_Last_Activity) based on the Subject in the Leads’ Activity History records?

    • The Activity History is actually a combination of both Task and Event – so you’re looking at two triggers to start. You can use a single class that you pass the list of objects to eliminate duplication of code.

      As you spin through the triggered record, you’ll want to make sure that you’re only looking at Activities related to a Lead (use the 3-character recordId prefix), and check the Subject line for your matching text.

      Some best practices to keep in mind:

      1 – The “Subject” values you want to match on, put them in a Custom Setting and parse them in to a List in your trigger/class.
      2 – Create an empty list to hold the Lead records you’re updating – one DML at the end of the class/trigger!
      3 – As you’re spinning through the triggered records, you can instanciate the Lead you want to update with:

      lstLeadsToUpdate.add(new Lead(Id=triggeredActivity.WhoId,Lead_Last_Activity__c=triggeredActivity.ActivityDate));

      Doing it through “creating” a new lead allows you to specify the Id – and not have to query to get those records beforehand.

      Give the trigger a try first, and if you get stuck post everything up on the Salesforce Stackexchange for more help!

      Thanks Hemu!

      -Andy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s