To date, triggers have only been accessible on each object's setup page, leading to a lot of hunting for code within the Salesforce CRM application. Even in Eclipse, switching between the Class and Trigger folders for a given project can be a pain.
These pains can partially be alleviated by keeping all Apex code in one place--as Apex Classes. With the addition of a consolidated trigger list in Summer09, some may feel that this post is superfluous, but consolidating code in one place, combined with the trigger list, will lead to a better development, debugging, and org administration experience.
First, here's a sample trigger (written by Jeff Douglas):
trigger AddOwnerColor on Account (before insert, before update) {
// create a set of all the unique ownerIds
Set<Id> ownerIds = new Set<Id>();
for (Account a : Trigger.new)
ownerIds.add(a.OwnerId);
// query for all the User records for the unique userIds in the records
// create a map for a lookup / hash table for the user info
Map<Id, User> owners = new Map<Id, User>([SELECT Favorite_Color__c FROM User WHERE Id IN :ownerIds]);
// iterate over the list of records being processed in the trigger and
// set the color before being inserted or updated
for (Account a : Trigger.new)
a.Owner_Favorite_Color__c = owners.get(a.OwnerId).Favorite_Color__c;
}
Let's pull the code from the trigger into an Apex Class and leave a reference to that class & method in the trigger. We need to pass the list Trigger.new as a parameter to the new class's method:
The trigger:
trigger AddOwnerColor on Account (before insert, before update) {
AccountTriggers.AddOwnerColor(Trigger.new);
}
And the class:
public class AccountTriggers {
public static void AddOwnerColor(Account[] accts) {
// create a set of all the unique ownerIds
Set<Id> ownerIds = new Set<Id>();
for (Account a : accts)
ownerIds.add(a.OwnerId);
// query for all the User records for the unique userIds in the records
// create a map for a lookup / hash table for the user info
Map<Id, User> owners = new Map<Id, User>([Select Favorite_Color__c from User Where Id in: ownerIds]);
// iterate over the list of records being processed in the trigger and
// set the color before being inserted or updated
for (Account a : accts)
a.Owner_Favorite_Color__c = owners.get(a.OwnerId).Favorite_Color__c;
} // close AddOwnerColor
}
While this may seem trivial, it has a few advantages:
- Easier to work in Eclipse (all code in the Classes section)
- Easier to write test code (can see tests and their associated methods in one place)
- Can promote code reuse by allowing other classes and triggers to call the same method.
- For those who like to include test code in the same class as the regular Class, this allows them to do so.
There's a catch (there always is):
You should comment into your Class which trigger is calling the class because otherwise, it is almost impossible to see at a glance where the code flows. This will especially help when writing and debugging tests.
Just a matter of personal style: It may be a good idea to write an Apex Class for each object's triggers (such as class AccountTriggers above). Code reuse is still possible, but it can track where triggers were originally used.
Happy coding!
Jeremy Ross says
I prefer to keep triggers very lean and free of business logic. I’d probably disagree with commenting your class with which triggers call it, for a few reasons. 1) The class shouldn’t know or care who calls it – the beauty of reuse, 2) someone will inevitably create a new client of the class and not update the “users” comment in the class, 3) this is related to 2, which is you’ll have a false sense of security of who is using the class.
The “show dependencies” button on the apex class screen would be infinitely more useful if it would show dependents instead of dependencies.
Scott Hemmeter says
I use this approach with a slight mod.
I have 1 trigger for each object and it fires for all events where I have code. Example of my Leads trigger below. It’s basically what you are suggesting, but it’s one common trigger and I add if/thens based upon the event that is firing.
This way there is 1 trigger and 1 class for each object.
trigger Leads on Lead (after insert, after update) {
// AFTER INSERT
if(Trigger.isAfter && Trigger.isInsert){
Leads l = new Leads();
l.CreateCampaignMembers(Trigger.new);
}
// AFTER UPDATE
if(Trigger.isAfter && Trigger.isUpdate){
Leads l = new Leads();
l.setOpportunityDefaults(Trigger.new, Trigger.oldMap);
l.transferOpptyFromLeadToAccount(Trigger.new, Trigger.oldMap);
}
}
David Schach says
I was just thinking about something similar today – with some logic and passing a String parameter, one could use the same class to update a single field to multiple values. For instance, an opportunity trigger to Account.Type -> Customer if total won opp value is >0, and -> VIP Customer if total won opp value > 250,000.
The possibilities are limitless!
Dave Manelski says
We have a few classes with methods used by multiple triggers. In lieu of passing the trigger set (sObjects), we’ve opted for passing primivitive data types only between triggers and classes, purposefully to reduce dependencies. In your example, I would probably opt for passing a list of strings (for the Ids in the trigger set) and perform a SOQL in the class.
By keeping these dependencies to a minimum, you can easier package code, among other benefits. I’ve adopted this “best practice” based on observation and advice of more talented coders than myself so I’d be curious to find out how someone with formal computer science training weighs in on this.
Jeff Douglas says
David, thanks for taking my crappy code and making it better. I won’t receive a bill will I? I find your reasoning sound and have been refactoring a number of triggers in this same way.