1

Not sure if this is a unit test related problem or not... but I'm trying to update a field before update using trigger.new. When I system.debug(trigger.new), I get null, even though the beforeUpdate() event is firing.

I'm using a generic trigger interface that kicks off a handler called OpportunityTriggerHandler. On the After Update method I have existing code that works, however, my code in the before update method is a little whacky. See code example below

public with sharing class OpportunityTriggerHandler implements ITrigger{
    public void beforeUpdate()
    {
                 
        //Assign Intacct_Entity_Id with Account State
        List<Opportunity> oppList = (List<Opportunity>)Trigger.new;
        List<Opportunity> oldList = Trigger.old;
        Set<String>requiredStates;
        List<Opportunity>oppsToUpdate;
        system.debug('This is the opplist: ' + oppList);
        system.debug('This is Trigger.new: ' + trigger.new);
        system.debug('This is (Opportunity)Trigger.new: '+ (List<Opportunity>)Trigger.new);
        //Look for field, if null add state to requiredStates set
        for(Opportunity opp :oppList){
            Opportunity oldopp = (Opportunity)Trigger.OldMap.get(opp.id);
            //Add requiredStates and update if Entity is blank or Account State changes
            if((opp.Intacct_Entity__c==null)||(oldopp.Account_State__c!=opp.Account_State__c)){
                if(opp.Account_State__c!=null){
                    requiredStates.add(opp.Account_State__c);
                    oppsToUpdate.add(opp);
                }
            }
        }
}


23:55:32.117 (2712535114)|USER_DEBUG|[41]|DEBUG|This is the opplist: null
23:55:32.117 (2712549186)|STATEMENT_EXECUTE|[42]
23:55:32.117 (2712554744)|HEAP_ALLOCATE|[42]|Bytes:21
23:55:32.117 (2712595697)|HEAP_ALLOCATE|[42]|Bytes:25
23:55:32.117 (2712609135)|USER_DEBUG|[42]|DEBUG|This is Trigger.new: null
23:55:32.117 (2712621243)|STATEMENT_EXECUTE|[43]
23:55:32.117 (2712626107)|HEAP_ALLOCATE|[43]|Bytes:34
23:55:32.117 (2712666160)|HEAP_ALLOCATE|[43]|Bytes:38
23:55:32.117 (2712679948)|USER_DEBUG|[43]|DEBUG|This is (Opportunity)Trigger.new: null

trigger:

trigger OpportunityTrigger on Opportunity (before update, after insert, after update, before delete) {
   Trigger_Setting__c ts = Trigger_Setting__c.getInstance();
   if(ts.Opportunity_Trigger__c){ 
       if(Trigger.isAfter || (Trigger.isBefore && Trigger.isDelete)){
        TriggerFactory.createHandler(OpportunityTriggerHandler.class);
       }
    } 
    
public with sharing class TriggerFactory
{
    /**
     * Public static method to create and execute a trigger handler
     *
     * Arguments:   Schema.sObjectType soType - Object type to process (SObject.sObjectType)
     *
     * Throws a TriggerException if no handler has been coded.
     */
    public static void createHandler(Type t)
    {
        // Get a handler appropriate to the object being processed
        ITrigger handler = getHandler(t);
         
        // Make sure we have a handler registered, new handlers must be registered in the getHandler method.
        if (handler == null)
        {
            throw new TriggerException('No Trigger Handler found named: ' + t.getName());
        }
         
        // Execute the handler to fulfil the trigger
        execute(handler);
    }
    
    /**
     * private static method to control the execution of the handler
     *
     * Arguments:   ITrigger handler - A Trigger Handler to execute
     */ 
    private static void execute(ITrigger handler)
    {
        // Before Trigger
        if (Trigger.isBefore)
        {
            // Call the bulk before to handle any caching of data and enable bulkification
            handler.bulkBefore();
            
            if (Trigger.isDelete) {
                handler.beforeDelete();
            }
            else if (Trigger.isInsert) {
                handler.beforeInsert();
            }
            else if (Trigger.isUpdate)
            {
                handler.beforeUpdate();
            }
        }
        else
        {
            // Call the bulk after to handle any caching of data and enable bulkification
            handler.bulkAfter();
            
            if (Trigger.isDelete) {
                handler.afterDelete();
            }
            else if (Trigger.isInsert) {
                handler.afterInsert();
            }
            else if (Trigger.isUpdate) {
                handler.afterUpdate();
                
            }else if(Trigger.isUnDelete){
                handler.afterUnDelete();
            }
        }
        
        // Perform any post processing
        handler.andFinally();
    }
}

Would this be better put in the after update method?

Do I need to explicitly update oppsToUpdate later in the method, or would that be handled automatically during the update event?

1
  • Please show the trigger itself that calls your handler.
    – Phil W
    Commented Oct 14, 2021 at 6:12

1 Answer 1

1

I'd rather let the trigger framework loop my records and have it call something like

for (SObject so : Trigger.old) {
    handler.beforeUpdate(so, Trigger.newMap.get(so.Id));
}

Then in beforeUpdate you only alter the value on the single opp. Nonetheless you should be able to look them up in Trigger.new at this point.

If the opp is in trigger context you don't need to explicitly do a DML call for it. Only if you SOQL other Opps not in context you need to do that.

It looks like your Trigger framework is faulty and calls your update method even though there is no actual Update DML running. So make sure your framework checks Trigger.isUpdate and Trigger.isBefore before calling your method. You can also debug that there are actual records in Trigger.new before calling the method and while you're at it also check Trigger.old.

3
  • You were right! The (Trigger.isBefore && Trigger.isDelete) part of the trigger was some old left over code from something else. We weren't using the isBefore for anything else and I didn't even think about it. I edited the logic and the isBefore calls correctly, and with the appropriate Trigger.new. Thank you!!!!
    – PaulyP
    Commented Oct 14, 2021 at 8:35
  • 1
    Calling a method on an individual record can be dangerous (an easy way to get yourself into a situation where you run queries and/or perform DML in a loop), so I wouldn't recommend this approach.
    – Derek F
    Commented Oct 14, 2021 at 11:40
  • Well...don't do that. A framework that splits records for you also provides bulk methods before and afterwards. Most triggers don't require additional related data, and if they do you're extending the trigger scope anyways on a record by record basis inside your for loop. If that doesn't turn on your red lights it's a lost cause either way. If you only give yourself one record to work with (in a DML-red-Zone) and have separate bulk method before/afterwards you got that separation engrained automatically, and become much more aware of the overhead as soon as you go to the bulk-methods.
    – Schnaps
    Commented Oct 18, 2021 at 11:36

You must log in to answer this question.

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