One Class to control all Trigger. – Dispatcher

I’m having some problems when trying to make a number of classes and triggers to run on a dispatcher class.
The components for this architecture are the following:

  1. One –TriggerDispatcher class.
  2. One trigger – MainAttachmentEntry
  3. One Apex class – AttachmentVisitReport

The first point for this architectural to work is the MainAttachmentEntry
Code bellow:

trigger MainAttachmentEntry on Attachment (after delete, after insert, after update, before delete, before insert, before update)
{

TriggerDispatcher.entry(new  TriggerDispatcher.TriggerParameters(Trigger.isBefore, Trigger.isAfter, Trigger.isDelete, Trigger.isInsert, Trigger.isUpdate, Trigger.isExecuting, Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap));
}

This is packet that contains all trigger context variables, in this case except after undelete.
This design is based on Adam Purkins dispatcher.

The MainAttachmentEntry trigger calls the TriggerDispatcher class and the entry() method.

Here is the TriggerDispatcher class:

public with sharing class TriggerDispatcher 
{

private static final String SACCOUNT = 'Account'; 
private static final String SCONTACT = 'Contact';
private static final String SLEAD = 'Lead';
private static final String SOPPORTUNITY = 'Opportunity';
private static final String SUSER = 'User'; 
private static final String SEVENT = 'Event'; 
private static final String SATTACHMENT = 'Attachment';
private static final String SFEEDITEM = 'FeedItem';

public static ITriggerEntry activeInstance = null; 
public static Map<Id, SObject> sObjectsToUpdate = new Map<Id, SObject>(); 



public interface ITriggerEntry
{
    void mainEntry(TriggerParameters tp);
    void inProgressEntry(TriggerParameters tp);     

}

public class TriggerParameters
{
    public String triggerObject {get; private set;}
    public Boolean isBefore {get; private set;}
    public Boolean isAfter {get; private set;}
    public Boolean isDelete {get; private set;}
    public Boolean isInsert {get; private set;}
    public Boolean isUpdate {get; private set;}
    public Boolean isExecuting {get; private set;}
    public List<SObject> oldList {get; private set;}
    public List<SObject> newList {get; set;}
    public Map<Id, SObject> oldMap {get; private set;}
    public Map<Id, SObject> newMap {get; set;}

    public TriggerParameters(Boolean ib, Boolean ia, Boolean id, Boolean ii, Boolean iu, Boolean ie,
        List<SObject> ol, List<SObject> nl, Map<Id, SObject> om, Map<Id, SObject> nm )
        {
            this.isBefore = ib;
            this.isAfter = ia;
            this.isDelete = id;
            this.isInsert = ii;
            this.isUpdate = iu;
            this.isExecuting = ie;
            this.oldList = ol; 
            this.newList = nl;
            this.oldMap = om;
            this.newMap = nm;
            this.triggerObject = getSObjType((this.oldList != null && this.oldList.size() > 0) ? 
            this.oldList[0] : this.newList[0]);
        } 

        private String getSObjType(SObject so)
        {
            String retVal; 
            if(so instanceof Account) retVal = SACCOUNT; 
            else if( so instanceof Contact) retVal = SCONTACT; 
            else if( so instanceof Lead) retVal = SLEAD; 
            else if( so instanceof Opportunity) retVal = SOPPORTUNITY;
            else if( so instanceof User) retVal =SUSER;
            else if( so instanceof Event) retVal = SEVENT; 
            else if( so instanceof Attachment) retVal = SATTACHMENT;
            else if( so instanceof FeedItem) retVal = SFEEDITEM; 
            return retVal; 

        }
}

//Central dispatch entry
public static void entry(TriggerParameters tp)
{
    if(activeInstance == null)
        processWork(tp);
        else
        activeInstance.inProgressEntry(tp);

}

//Order of execution is controlled

private static void processWork(TriggerParameters tp)
{
    if(tp.triggerObject == SATTACHMENT && tp.isAfter && tp.isInsert)
    {

      execute(new AttachmentVisitReport(), tp);

    }

      if(sObjectsToUpdate.size() >0)
            update sObjectsToUpdate.values();

}

private static void execute(ITriggerEntry ite, TriggerParameters tp)
{

    activeInstance = ite;
    activeInstance.mainEntry(tp);
    }
}

The entry() actually will pass the TriggerParametes tp and also make sure that this is the first time we are running those values within the TriggerParameters. If that is the case it will jump to another method, processWork(). There I can add the criteria for the using and if statement. In this case we are making sure that if the object passed on the TriggerParametes is of type Attachment, to execute a class AttachmentVisitReport.

Here is the code for AttachmentVisitReport. This class implements ITriggerEntry interface which it is inside the TriggerDispatcher class:

public with sharing class AttachmentVisitReport implements TriggerDispatcher.ITriggerEntry
{

public void inProgressEntry(TriggerDispatcher.TriggerParameters tp)
{
    //Logic .....
}

public void mainEntry(TriggerDispatcher.TriggerParameters tp)
{

    Map<Id, Attachment> newMap = (Map<Id, Attachment>) tp.newMap;
    Map<Id, Attachment> attMap = new Map<Id, Attachment>([SELECT ParentId FROM Attachment WHERE Parent.Type = 'Event' AND Id IN :newMap.keySet()]);
    Set<Id> eveIds = new Set<Id>(); //Event Ids
    Set<Id> accIds = new Set<Id>(); // Account Ids for Events  
    Set<Id> ownIds = new Set<Id>(); // Event OwnerIds 

    for(Attachment a: attMap.values())

        if(!attMap.isEmpty())

        eveIds.add(a.ParentId); 


    Map<Id, Event> eveMap = new Map<Id, Event>([SELECT OwnerId, WhatID FROM Event WHERE Id IN : eveIds]);

    for(Event e : eveMap.values())
    {
        if(!eveMap.isEmpty())

        accIds.add(e.WhatId); 
        ownIds.add(e.OwnerId); 


    List<EntitySubscription> entityListAcct = [SELECT id, ParentId, SubscriberId FROM EntitySubscription WHERE ParentId IN :accIds];
    List<EntitySubscription> entityListOwn = [SELECT id, ParentId, SubscriberId FROM EntitySubscription WHERE ParentId IN :ownIds];

    for( EntitySubscription en :entityListAcct)
    {
        if(!entityListAcct.isEmpty())
        {
        FeedItem feedItemAcct = new FeedItem(
        Body='Update done to Visit Report. Attachment inserted or updated',
        ParentId = en.ParentId);

        insert feedItemAcct;  


        TriggerDispatcher.sObjectsToUpdate.put(feedItemAcct.id , feedItemAcct);
         }
       }
     }
   }
 }

The intention of this code is to add a Feed for those following the Owner of events if those contain an attachment. It also tries to do the same for Accounts followers if there is an Event related to an Account.
Actually he logic is:

If an Event has WhatId = Account and an Attachment is inserted in the Event, a new feed should be send to the Event owner followers and to the Account followers.

I tried different things but is not inserting the Feed. On this code I’m just trying to do this which new attachments and only for Account followers.
This section:

for( EntitySubscription en :entityListAcct)
    {
        if(!entityListAcct.isEmpty())
        {
        FeedItem feedItemAcct = new FeedItem(
        Body='Update done to Visit Report. Attachment inserted or updated',
        ParentId = en.ParentId);

        insert feedItemAcct;  


        TriggerDispatcher.sObjectsToUpdate.put(feedItemAcct.id , feedItemAcct);

I tried to change the SObjectToUpdate to do a insert rather than update … but is not inserting the Feed. So, can anyone give some directions on how to make this work?
Thanks.

Answer

I’m not sure I can help you fix your code, but maybe you’ll be interested in this design pattern that accomplishes pretty much the same thing. I’m not sure who the original author is, but it’s included in the MavensMate sublime text plugin as a class template.

First, add this universal “TriggerHandler” utility class to your code base:

//This is a utitliy class that can be used to help manage triggers.  
// For each object, create 1 "Master" Trigger that can handle each event.  Then for each peice of funcitonality,
// create a class and implement "TriggerHandler.HandlerInterface".  Write all the trigger logic in the "void handle()"
// method just as you would directly in a trigger.  The one difference is that Trigger.new and Trigger.old must be explicitly cast to the concrete SOBJECT
// EX:
//  * for(System__c sys : (List<System__c>) trigger.new){}
//  * Account old = (Account) trigger.old[i];
//In the master trigger:
// 1: Instante a new Trigger Handler Obj: TriggerHandler handler = new TriggerHandler();
// 2: Bind any logic to the desired events: handler.bind(TriggerHandler.Evt.beforeupdate, new MyHandlerClass());
//   Make sure that the event bound is executed by the trigger!!!
// 3: call manage: handler.manage();
public class TriggerHandler 
{
    /**
     *   Enum representing each of before/after CRUD events on Sobjects
     */
    public enum Evt 
    {
        afterdelete, afterinsert, afterundelete,
        afterupdate, beforedelete, beforeinsert, beforeupdate   
    }

    /*
     *   Simplistic handler to implement on any of the event. It doesn't require or enforces any pattern except the
     *   method name to be "handle()".
     */
    public interface HandlerInterface
    {
        void handle();          
    } 

    // Internal mapping of handlers
    Map<String, List<HandlerInterface>> eventHandlerMapping = new Map<String, List<HandlerInterface>>();

    /**
     *   Core API to bind handlers with events
     */
    public TriggerHandler bind(Evt event, HandlerInterface eh)
    {
        List<HandlerInterface> handlers = eventHandlerMapping.get(event.name());
        if (handlers == null) 
        {
            handlers = new List<HandlerInterface>();
            eventHandlerMapping.put(event.name(), handlers);
        }
        handlers.add(eh);
        return this;
    }

    /**
     *   Invokes correct handlers as per the context of Trigger and available registered handlers
     */
    public void manage()
    {
        Evt ev = null;
        if(Trigger.isInsert && Trigger.isBefore)
        {
            ev = Evt.beforeinsert;
        }
        else if(Trigger.isInsert && Trigger.isAfter)
        {
            ev = Evt.afterinsert;
        }
        else if(Trigger.isUpdate && Trigger.isBefore)
        {
            ev = Evt.beforeupdate;
        }
        else if(Trigger.isUpdate && Trigger.isAfter)
        {
            ev = Evt.afterupdate;
        }
        else if(Trigger.isDelete && Trigger.isBefore)
        {
            ev = Evt.beforedelete;
        }
        else if(Trigger.isDelete && Trigger.isAfter)
        {
            ev = Evt.afterdelete;
        }
        else if(Trigger.isundelete)
        {
            ev = Evt.afterundelete;             
        }

        List<HandlerInterface> handlers = eventHandlerMapping.get(ev.name());

        if (handlers != null && ! handlers.isEmpty()) 
        {
            for (HandlerInterface h : handlers) 
            {
                h.handle();
            }
        }
    }

}

For each operation your want to run, create implement the handler interface:

public with sharing class MyAttachmentOperation1 implements TriggerHandler.HandlerInterface {
    public void handle(){
       for(Attachment attach : (List<Attachment>) Trigger.new){
       //do something...
       }
    }
}

Finally create your master trigger for attachment:

//Master trigger for all events on attachment.
// Each individual action should implement TriggerHandler.HandlerInterface
// If binding a new event, make sure that it is uncommented from the trigger def
//See comments in TriggerHandler for more details on the pattern
trigger AttachmentMasterTrigger on Attachment (
    before update,
    after insert,
    before insert
    //after update, 
    //before delete, 
    //after delete, 
    //after undelete
    ) {

    //add action for project license date trigger handler
    TriggerHandler th = new TriggerHandler();
    th.bind(TriggerHandler.Evt.beforeinsert, new MyAttachmentOperation1());
    th.bind(TriggerHandler.Evt.beforeupdate, new MyAttachmentOperation2());
    th.bind(TriggerHandler.Evt.afterinsert, new MyAttachmentOperation3());

    //execute bindings
    th.manage();

}

Attribution
Source : Link , Question Author : Carlos Naranjo , Answer Author : NSjonas

Leave a Comment