Sunday, 24 February 2019

Trigger Design Pattern in Apex


Trigger Design Patterns Without Framework :
=================================

Frameworks improve reliability and maintainability,and make probelms easier to debug.

ex :
1.Follow up on lead account(Process Builder)
a. Insert lead with 'Open not Contacted' with good email
b. Create a follow up task.

2.taskTriggerSetStatus
a.Task created for lead
b.if the lead has a task, set its status to 'Working-Contacted'
or 'Working - harder '.

3.leadTriggerSetFollowup
a.if a lead has a working status
b.Create a follow-up task (reminder).

4.taskTriggerTrackCount
a.Task is created,updated or deleted
b.Update # of tasks on the lead

5.First Owner Worked (Process Builder)
a.Lead status starts with Working and Owner is a user.
b.Set first owner worked field.

Note :
=========
1.controling the trigger order
2.Combining DML operations
3.controlling reentrancy


Dynamic Trigger Handler :
===========================

public interface ITriggerExtension
{

  void HandlerTrigger(TriggerOperation operationType,
              List<SObject> newList,List<SObject> oldList,
              Map<ID,SObject> newMap,Map<ID,SObject> oldMap);
}


public with sharing class dynamicTaskTriggerHandler implements ITriggerExtension{

public void HandleTrigger(TriggerOperation operationType,List<SObject> newList,List<SObject> oldList,
                         Map<ID,SObject> newMap,Map<ID,SObject> oldMap)
 {
   system.debug('Trigger execution was called');
 }

}


trigger taskTrigger on Task (before insert,after insert,before update,after update,before delete,after delete){

simpleLeadUpdater leadUpdater = new simpleLeadUpdater();

// What happens when you change the order of these two triggers handlers?

  if(trigger.operationType == TriggerOperation.AFTER_INSERT)
    {
      taskSetStatus.handleTrigger(trigger.new,leadUpdater);
    }

  if(trigger.operationType == TriggerOperation.AFTER_INSERT ||
     trigger.operationType == TriggerOperation.AFTER_UPDATE ||
     trigger.operationType == TriggerOperation.AFTER_DELETE )
     {
     
       taskTrackCount.handleTrigger (trigger.operationType,trigger.new,trigger.old,leadUpdater);
    }

 List<ITriggerExtension> dynamicTriggers = TriggerExtensionSupport.getTriggerClasses('Task');
  for(ITriggerExtension trig : dynamic Triggers)
   {
      trig.HandleTrigger(trigger.operationType,trigger.new,trigger.old,trigger.newMap,trigger.oldMap);
 
   }

  leadUpdater.updateLeads();
}

public with sharing class TriggerExtensionSupport{

public static List<ITriggerExtension> getTriggerClasses(String objectType)
 {
    List<Dynamic_Trigger_mdt> triggerSettings = [ Select Class_Name__c from Dynamic_Trigger__mdt where
                                                  Object_Type__c = : objectType OrderBy Priority__c ASC];
    List<ITriggerExtension> results = new List<ITriggerExtension>();

    for(Dynamic_Trigger__mdt setting : triggerSettings)
    {
      System.Type thetype = Type.forName(setting.Class_Name__c);
       if(thetype == null)
        thetype = Type.forName('',setting.Class_Name__c); // Try resolving local class
       if(thetype!=null){
           Object theobject = thetype.newInstance();
           if(theobject instanceof ITriggerExtension) results.add ((ITriggerExtension) theobject);
        }
    }

   return results ;
 }

}

public with sharing class taskTrackCount {

public static void handleTrigger (TriggerOperation operationType, List<Task> newList,List<Task> oldList,simpleLeadUpdater leadUpdater)
  {
     Set<ID> leadIds = new Set<ID>();
   
     if(operationType == TriggerOperation.AFTER_UPDATE || operationType == TriggerOperation.AFTER_INSERT)
       {
          for(Task t : newList)
           {
             if(t.WhoId!=null && t.WhoId.getSObjectType()==Schema.Lead.SObjectType)
              leadIds.add(t.WhoId);
           }
       }
 
     if(operationType == TriggerOperation.AFTER_UPDATE || operationType == TriggerOperation.AFTER_DELETE)
         {
            for(Task t : oldList)
             if(t.whoId !=null && t.whoID.getSObjectType() == Schema.Lead.SObjectType)
               leadIds.add(t.whoId);
         }

     List<Lead> leads = [Select ID,Task_Count__c from Lead where ID in : leadIds];
     List<AggregateResult> tasks = [Select Count(ID) items,WhoId from Task where WhoId in : leadIds group by WhoID];
     Map<ID,Interger> taskCounts = new Map<ID,Interger>();
 
     for(AggregateResult ar : tasks)
     {
       taskCounts.put((ID)ar.get('WhoId'),(Interger) ar.get('items));
     }
   
     for(Lead ld : leads)
      {
        if(ld.Task_Count__c != taskCounts.get(ld.Id))
          {
            Lead toUpdate = leadUpdater.getLead(ld.id);
            toUpdate.Task_Count__c = taskCounts.get(ld.id);
          }
      }
   }
}

public with sharing class taskSetStatus {

private static Set<ID> statusUpdated = new Set<ID>();

public static void handleTrigger (List<Task> newTasks,simpleLeadUpdater leadUpdater){

    Set<ID> leadIds = new Set<ID>();

     for(Task t : newTasks)
      {
        if(t.whoId!=null && t.whoId.getSObjectType() == Schema.Lead.SObjectType && !t.Declarative_Created__c)
            leadIds.add(t.whoId);
      }

    List<Lead> leads = [Slect ID,Status from Lead where ID in : leadids];

   for(Lead ld : leads)
    {
       if(statusUpdated.contains(ld.id)) continue; // Skip those already updated

       switch on ld.status
        {
           when 'Open - Not Contacted'
            {
              Lead toUpdate = leadUpdater.getLead(ld.id);
               toUpdate.status = 'Working - Contacted ';
               statusUpdated.add(ld.id);
            }
           when 'Working - Contacted'
            {
              Lead toUpdate = leadUpdater.getLead(ld.id);
               toUpdate.status = 'Working Harder ';
                statusUpdated.add(ld.id);
            }
        }
    }
 }

}

public with sharing class simpleLeadUpdater {
  Map<ID,Lead> leadsToUpdate = new Map<ID, Lead>();

 public Lead getLead(ID leadID)
  {
    Lead targetLead = leadsToUpdate.get(leadID);
     if(targetLead == null)
      {
        targetLead = new Lead (ID = leadID);
         leadsToUpdate.put(leadID,targetLead);
      }
    return tragetLead;
  }

public void updateLeads()
  {
    if(leadsToUpdate.size()>0)
      update leadsToUpdate.values();
  }

}

Test Class :
===============
@istest
public with sharing class TestLeadActions{

@istest
public static void TestInsertSingleLead(){
    Test.startTest();
    InsertLeads(1,'Open-Not Contacted');
    Test.stopTest();
  }

@isTest
public static void TestInsertBulkLead()
 {
   Long startTime = DateTime.now().getTime();
   Long startCPU = Limits.getCpuTime();
   Test.startTest();
   InsertLeads(200,'Open - Not Contacted');
   Test.stopTest();
   system.debug ('Elapsed : ' + (Datetime.Now().getTime() - startTime));
   system.debug ('Elapsed CPU : ' + (Limits.getCpuTime() - startCPU));

 }

public static void InsertLeads(Interger count,String leadstatus)
{
  List<Lead> leadsToInsert = new List<Lead>();
  for(Interger x=0;x<count;x++)
   {
      String xs = string.valueOf(x);
      leadsToInsert.add(new Lead(FirstName = 'f' +xs,LastName = 'l'+xs,
                         status=leadstatus,
                         Company = 'c' + xs,
                         email = 'e' + 'xs' + '@pluralsight.com',
                         Bypass_Declarative__c = 0 ));

   }
   insert leadsToInsert;
}

 Public static void UpdateLeadStatus()
  {
    List<Lead> leads = [ select Id, status from Lead ];
     for(Lead ld : leads)
       ld.status = 'Working - Contacted ';
     Test.startTest();
      update leads ;
    Test.stopTest();

  }
}

No comments:

Post a Comment