0

I have a trigger and helper class (both below) on a custom object. I've been told that I should have a trigger, helper class, and a utility class as a best practice, but I have not used utility classes before. Does the code below require a utility class and if so how would I need to update my existing trigger and helper class?

Trigger:

trigger VTrigger on V_Type__c (after insert, after update, after delete) {

    if(Trigger.isAfter && Trigger.isInsert){
        VTriggerHandler.onAfterInsUp(Trigger.new);
    }
    if(Trigger.isAfter && Trigger.isUpdate){
        VTriggerHandler.onAfterInsUp(Trigger.new);
    }
    if(Trigger.isAfter && Trigger.isDelete){
        VTriggerHandler.onAfterDelete(Trigger.old);
    }
}

Helper Class:

public class VTypeTriggerHandler {
    
    public static void onAfterDelete (List<V_Type__c> vType){
        
        // Sets for related Account & Contact IDs
        Set<Id> acctIds = new Set<ID>();
        Set<Id> contIds = new Set<ID>();
        
        // Maps for V Types
        Map<V_Type__c, V_Type__c> contactTypeMap = new Map<V_Type__c, V_Type__c>();
        Map<Id, Set<String>> contactTypeMapDel = new Map<Id, Set<String>>();
        
        // Get all the Account & Contact Ids in the Set being removed
        for(V_Type__c v : vType){
            acctIds.add(v.Account__c);
            contIds.add(v.Contact__c);
            contactTypeMapDel.put(v.Contact__c , new set<String>());
        }
        
        
        // Query the Accounts
        List<Account> acct = new List<Account>();
        
        // Use the V Types to get all the related Types for the Account 
        acct = [SELECT Id, V_Types__c,(Select V_Type__c FROM V_Types__r ORDER BY V_Type__c) 
                FROM Account 
                WHERE Id in :acctIds]; 
        
        // Iterate over each Account and V record 
        for(Account a : acct){ 
            a.V_Types__c = ''; 
            
            for(V_Type__c v: a.V_Types__r){ 
                
                if(!a.V_Types__c.contains(v.V_Type__c) || a.V_Types__c == ''){ // Check if the Type is already in the Account Field. if not add it otherwise skip 
                    
                    a.V_Types__c += v.V_Type__c + '; '; 
                } 
            } 
        } 
        // Update the Account 
        update acct; 
    }
    
    
    public static void onAfterInsUp (List<V_Type__c> vType){
        
        // Sets for related Account & Contact IDs
        Set<Id> acctIds = new Set<ID>();
        Set<Id> contIds = new Set<ID>();
        
        // Maps for V Types
        Map<V_Type__c, V_Type__c> contactTypeMap = new Map<V_Type__c, V_Type__c>();
        Map<Id, Set<String>> contactTypeMapDel = new Map<Id, Set<String>>();
        
        // Get all the Account & Contact Ids in the Set being removed
        for(V_Type__c v : vType) {
            acctIds.add(v.Account__c);
            contIds.add(v.Contact__c);
        }
        
        List<V_Type__c> vRecs = [SELECT Id,Account__c,Contact__c,V_Type__c
                                     FROM V_Type__c
                                     WHERE Contact__c = :contIds AND ID NOT IN:vType];
        
        //Check for V Types already entered for Contact
        Map<Id,set<string>> existingTypeMap = new Map<Id,set<string>>();
        
        for(V_Type__c v: vRecs) {
            set<string> existingTypes = new set<string>();
            if(existingTypeMap.containsKey(v.Contact__c)){
                existingTypes = existingTypeMap.get(v.Contact__c);
            }
            existingTypes.add(v.VIP_Type__c);
            existingTypeMap.put(v.Contact__c,existingTypes);
        }
        
        for (V_Type__c v : vType)
        {
            if (existingTypeMap.containsKey(v.Contact__c)){
                if (existingTypeMap.get(v.contact__c).contains(v.V_Type__c)){
                    trigger.newMap.get(v.Id).addError('The V Type of '+v.V_Type__c+' already exists for this Contact');
                }
                else
                {
                    Set<string> existingTypes = existingTypeMap.get(v.Contact__c);
                    existingTypes.add(v.V_Type__c);
                    existingTypeMap.put(v.Contact__c,existingTypes);
                }
            }
        } 
        
        
        // Query the Accounts
        List<Account> acct = new List<Account>();
        
        // Use the V Types to get all the related Types for the Account 
        acct = [SELECT Id, V_Types__c,(Select V_Type__c FROM V_Types__r ORDER BY V_Type__c) 
                FROM Account 
                WHERE Id in :acctIds]; 
        
        // Iterate over each Account and V record 
        for(Account a : acct){ 
            a.V_Types__c = ''; 
            
            for(V_Type__c v: a.V_Types__r){ 
                
                if(!a.V_Types__c.contains(v.V_Type__c) || a.V_Types__c == ''){ // Check if the Type is already in the Account Field. if not add it otherwise skip 
                    
                    a.V_Types__c += v.V_Type__c + '; '; 
                } 
            } 
        } 
        // Update the Account 
        update acct; 
    }
}
3
  • 2
    To my knowledge, a Utility class is something which can be used across multiple classes. For instance, we can have a TestUtility class where we can create/setup test data and can be used across all Unit test classes within the org. In your context, you would need a Utility class when there is a need for same data structures across multiple implementations within an org.
    – SatyaV
    Commented Oct 27, 2020 at 16:16
  • Thanks. I'm not sure why or how I would need a utility class for the handler class above.
    – J. Neilan
    Commented Oct 27, 2020 at 17:51
  • 1
    my preferred pattern is domain class, service class, selector class - as epitomized by this Enterprise Patterns Trailmix
    – cropredy
    Commented Oct 27, 2020 at 21:36

1 Answer 1

1

So in the above classes if certain pieces of code that you want to use a piece of code for example like calculate total that is called in both of the methods then this piece of code is created as a method in the utility class so that there is no repetitive code.

Also, I would suggest try changing the below code from

if(Trigger.isAfter && Trigger.isInsert){
        VTriggerHandler.onAfterInsUp(Trigger.new);
    }
    if(Trigger.isAfter && Trigger.isUpdate){
        VTriggerHandler.onAfterInsUp(Trigger.new);
    }

to

if(Trigger.isAfter && (Trigger.isInsert|| Trigger.isUpdate)){
            VTriggerHandler.onAfterInsUp(Trigger.new);
        }

You can have a look at these links that can help:

1> what-is-the-best-way-to-structure-an-apex-trigger

2> what-are-the-best-practices-for-triggers

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .