Write your first APEX trigger – livecoding.tv stream!

Well heck, that was pretty cool.  As part of Salesforce Developer Champions, I set up a streaming channel on Livecoding.TV – a new service dedicated to streaming developers developing stuff.

My first experience was pretty slick – I broadcast live to about 40 people how to create your first APEX trigger.  You can watch the recording (it’s in two parts because my connection dropped) at https://www.livecoding.tv/techman97.

Favorite my channel for more streams coming soon!

Advertisements

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.  =)

Deleting a record from its own update trigger

Here’s a nice little one a colleague and I stumbled on today – we had a requirement where we needed to delete an Event if the Event’s status was updated to a specific value.

Going at a deletion the “usual way”, the trigger barked at us that we couldn’t do any DML on the triggered records themselves…so voila, here we go:

if(trigger.isUpdate) {

List<Event> lstEventsToDelete = new List<Event>();

for(Event e : trigger.new) {
Event eDel = new Event(Id=e.Id);
lstEventsToDelete.add(eDel);
}

delete lstEventsToDelete;

}

Nice little “workaround” for deleting the same record you’re triggering on.  =)

Comparing new values vs. old values in APEX Trigger

I came across a situation today where I needed to compare new vs. old values in an APEX Trigger to determine a course of action.  Here is a slick little way to do this without having to worry about if Salesforce decided to correctly order your trigger.old in the same way that trigger.new is.

Here is the APEX Trigger:

trigger historyCheckerLogic on Event (before update)

///////////////////////////////////////
// This trigger handles record updates from the EVENT object
// Reference:  (class) historyChecker.checkPreviousValues()
// (other nice little header info)
///////////////////////////////////////

// Declare APEX Class where work will be done (I try not to ever do any work inside of a trigger)
public historyChecker clsHistoryChecker = new historyChecker();

// Put trigger.new and trigger.old data into Lists declared in my APEX Class
clsHistoryChecker.lstNewEvents = Trigger.new;
clsHistoryChecker.lstOldEvents = Trigger.old;

// Trigger Logic – Before Update
if(trigger.isBefore && trigger.isUpdate) {
clsHistoryChecker.checkPreviousValues();
}

}

Here is the APEX Class:

public class historyChecker {

///////////////////////////////////////
// This class handles logic from the historyCheckerLogic trigger
// (other nice little header info)
///////////////////////////////////////

public List<Event> lstNewEvents = new List<Event>();
public List<Event> lstOldEvents = new List<Event>();

public void checkPreviousValues() {

///////////////////////
// This method compares old values to new values from the historyCheckerLogic trigger
///////////////////////

// Declare method variables
Map<Id, Event> mapVerifyOldEvents = new Map<Id, Event>();

// Create initial Map of old Events
for(Event evtOld : lstOldEvents) { mapVerifyOldEvents.put(evtOld.Id, evtOld); }

// Loop through lstNewEvents and check old values
for(Event evtNew : lstNewEvents) {

if(evtNew.ActivityDate != mapVerifyOldEvents.get(evtNew.Id).ActivityDate) {

// The values are NOT the same, do whatever logic you want…
evtNew.addError(‘Ummm, you can’t change this field!’);

}

}

}

}

I remember looking high and low for a way to efficiently do this a while back – so hopefully if you’ve found my page I’ve helped you out a little with your question.  Please feel free to ask me other related questions in the comments below.

Thanks for readying and happy coding!