Created
November 26, 2012 15:11
-
-
Save cyberjus/4148705 to your computer and use it in GitHub Desktop.
SFDC Trigger Template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trigger Account_Trigger on Account (after delete, after insert, after undelete, | |
after update, before delete, before insert, before update) { | |
TriggerManager.execute('AccountTriggerHandler'); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class AccountTriggerHandler extends TriggerHandler { | |
// Build method to bind events to functions | |
public void build() { | |
System.debug('Build Account Trigger'); | |
bind(TriggerManager.Evt.AfterUpdate, new AccountAfterUpdateHandler()); | |
bind(TriggerManager.Evt.AfterUpdate, new UpdateAccount()); | |
} | |
/** | |
Subclasses to define what functions to run | |
*/ | |
// TEMP | |
private class AccountAfterUpdateHandler extends TriggerHandler.TriggerFunction { | |
public void main() { | |
System.debug('Trigger Items' + Trigger.new); | |
System.debug(LoggingLevel.INFO, 'After Update handling ' + Trigger.new ); | |
List<Contact> contacts = new List<Contact>([select Id, Title from Contact where AccountId in :Trigger.new]); | |
for (Contact c : contacts) { | |
c.Title = 'Fact Checker'; | |
} | |
update contacts; | |
} | |
} | |
// TEMP | |
private class UpdateAccount extends TriggerHandler.TriggerFunction { | |
public void main() { | |
List<Account> accounts = new List<Account>(); | |
for (SObject so : Trigger.new) { | |
Account account = (Account)so; | |
Account updateAccount = new Account(Id = account.Id); | |
updateAccount.Description = 'This is a test'; | |
System.debug('I updated the account'); | |
accounts.add(updateAccount); | |
} | |
unbind(TriggerManager.Evt.AfterUpdate); | |
update accounts; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public with sharing abstract class TriggerHandler { | |
// Internal mapping of handlers | |
Map<String, List<TriggerFunction>> eventFunctionMapping = new Map<String, List<TriggerFunction>>(); | |
// Force build on handler creation | |
public TriggerHandler() { | |
build(); | |
} | |
// Abstract Method to be implemented by Object Handlers | |
abstract void build(); | |
// Bind functions to events | |
public void bind(TriggerManager.Evt e, TriggerFunction f) { | |
List<TriggerFunction> functions = eventFunctionMapping.get(e.name()); | |
if (functions == null) { | |
functions = new List<TriggerFunction>(); | |
eventFunctionMapping.put(e.name(), functions); | |
} | |
functions.add(f); | |
} | |
// Unbind a function from the handler | |
public void unbind(TriggerManager.Evt e, TriggerFunction f) { | |
List<TriggerFunction> functions = eventFunctionMapping.get(e.name()); | |
if (functions != null) { | |
for (Integer i = 0; i <= functions.size(); i++) { | |
if (functions.get(i) == f) { | |
functions.remove(i); | |
} | |
} | |
} | |
} | |
// Invokes correct handlers as per the context of trigger and available registered handlers | |
public void run() { | |
TriggerManager.Evt e = null; | |
if(Trigger.isInsert && Trigger.isBefore){ | |
e = TriggerManager.Evt.BeforeInsert; | |
} else if(Trigger.isInsert && Trigger.isAfter){ | |
e = TriggerManager.Evt.AfterInsert; | |
} else if(Trigger.isUpdate && Trigger.isBefore){ | |
e = TriggerManager.Evt.BeforeUpdate; | |
} else if(Trigger.isUpdate && Trigger.isAfter){ | |
e = TriggerManager.Evt.AfterUpdate; | |
} else if(Trigger.isDelete && Trigger.isBefore){ | |
e = TriggerManager.Evt.BeforeDelete; | |
} else if(Trigger.isDelete && Trigger.isAfter){ | |
e = TriggerManager.Evt.AfterDelete; | |
} else if(Trigger.isUndelete){ | |
e = TriggerManager.Evt.AfterUndelete; | |
} | |
List<TriggerFunction> functions = eventFunctionMapping.get(e.name()); | |
if (functions != null && !functions.isEmpty()) { | |
// Run each applicable function | |
for (TriggerFunction f : functions.clone()) { | |
f.main(this); // Inject handler to the function | |
} | |
} | |
} | |
// Base Trigger Function. To be bound to events. | |
public abstract class TriggerFunction { | |
TriggerHandler handler; | |
// Main method injection to add the handler | |
public void main(TriggerHandler h) { | |
handler = h; | |
main(); | |
} | |
// Main Method that must be implemented by Trigger Functions | |
abstract void main(); | |
// Remove this function from the handler for an event | |
protected void unbind(TriggerManager.Evt e) { | |
handler.unbind(e, this); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public with sharing class TriggerManager { | |
// Enum representing each of before/after CRUD events on Sobjects | |
public enum Evt { | |
AfterDelete, AfterInsert, AfterUndelete, | |
AfterUpdate, BeforeDelete, BeforeInsert, BeforeUpdate | |
} | |
// Map of what handlers we have registered with their functions already | |
static Map<String, TriggerHandler> handlers = new Map<String, TriggerHandler>(); | |
// Static method called from trigger event | |
public static void execute(String name) { | |
// Get handler from registered list | |
TriggerHandler handler = handlers.get(name); | |
// Check if handler not registered | |
if (handler == null) { | |
handler = createHandler(name); | |
// Make sure we find a handler | |
if (handler == null) { | |
throw new TriggerHandlerException('No Trigger Handler registered for : ' + name); | |
} | |
handlers.put(name, handler); | |
} | |
// Run trigger handler | |
handler.run(); | |
} | |
// Private static method to get the appropriate handler for the object type. | |
private static TriggerHandler createHandler(String name) { | |
Type t = Type.forName(name); | |
return (TriggerHandler) t.newInstance(); | |
} | |
// Trigger Exception | |
public class TriggerHandlerException extends Exception {} | |
} |
Looks fine to me, more of a personal taste on how we want to go around it. Few suggestions
- TriggerManager is TriggerExecutor actually, as its not managing anything
- TriggerHandler could be using Builder pattern, so this would lead to a TriggerBuilder with a Trigger instance ready to be executed by TriggerExecutor http://en.wikipedia.org/wiki/Builder_pattern#Java
- main() is a starting class function name in Java, so being from that background it looks confusing to me.
- We don't need dynamic type initalization here, as we can change TriggerManager.execute (String) to TriggerManager.execute(TriggerHandler) and Account trigger can easily call TriggerManager.execute(new AccountTriggerHandler()), this will retain references correctly between classes ( a problem with dynamic string based initalizations).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And ideas from http://developer.force.com/cookbook/recipe/trigger-pattern-for-tidy-streamlined-bulkified-triggers