function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Alaric WimerAlaric Wimer 

Need help to bulkify a simple trigger

I have a trigger that runs after a PO Number record is created. It finds the record's related Opportunity and updates the PO Number lookup field on the Opportunity with the PO Number that fired the trigger. 

 

Everything works fine except that it's not bulkified. SOQL and DML in a for loop. I'm having difficulty understanding how to bulkify something like this. I know collections are needed but I'm not sure how to use them correctly.

 

Can anyone please help me understand a step by step process of how to bulkify something like this trigger? Any help is greatly appreciated.

 

trigger updateOppPoNumber on PO_Number__c (after insert) {
    for (PO_Number__c poNum : Trigger.new) {
        if (poNum.Id != null && poNum.Opportunity__c != null) {
            // Get related Opportunity
            List<Opportunity> opp = [
                SELECT Id, PO_Number__c
                  FROM Opportunity
                 WHERE Id = :poNum.Opportunity__c
                 LIMIT 1
            ];

            // Update related Opportunity with PO Number
            if (!opp.isEmpty()) {
                opp.get(0).PO_Number__c = poNum.Id;
                update opp;
            }
        }
    }
}
Best Answer chosen by Alaric Wimer
MTBRiderMTBRider
You can do something like below.  Note not tested so might be typos, but should give you the general idea...
 
trigger updateOppPoNumber on PO_Number__c (after insert) {
   Map<String, String> oppIdToPONum = new  Map<String, String>();
   for (PO_Number__c poNum : Trigger.new) {
        if (poNum.Id != null && poNum.Opportunity__c != null) {
           oppIdToPONum.put(poNum.Opportunity__c, poNum.Id);    //Create a map with the opp Id as  the key and the PO num as the value
        }
    }

   If ( !oppIdToPONum.isEmpty() ) {
           List<Opportunity> opp = [ SELECT Id, PO_Number__c FROM Opportunity WHERE Id IN :oppIdToPONum.keySet()];  //Get all opp records at once using an IN clause and the keys in the map
           
           if (!opp.isEmpty()) {
                for (Opportunity o : opp) {
                       o.PO_Number__c = oppIdToPONum.get(o.Id);     //loop over the opps returned above & update the PO # or each
                } 
                 update opp;            //Make a single update call to update all of the opp recs
            }
   }
        
}

 

All Answers

MTBRiderMTBRider
You can do something like below.  Note not tested so might be typos, but should give you the general idea...
 
trigger updateOppPoNumber on PO_Number__c (after insert) {
   Map<String, String> oppIdToPONum = new  Map<String, String>();
   for (PO_Number__c poNum : Trigger.new) {
        if (poNum.Id != null && poNum.Opportunity__c != null) {
           oppIdToPONum.put(poNum.Opportunity__c, poNum.Id);    //Create a map with the opp Id as  the key and the PO num as the value
        }
    }

   If ( !oppIdToPONum.isEmpty() ) {
           List<Opportunity> opp = [ SELECT Id, PO_Number__c FROM Opportunity WHERE Id IN :oppIdToPONum.keySet()];  //Get all opp records at once using an IN clause and the keys in the map
           
           if (!opp.isEmpty()) {
                for (Opportunity o : opp) {
                       o.PO_Number__c = oppIdToPONum.get(o.Id);     //loop over the opps returned above & update the PO # or each
                } 
                 update opp;            //Make a single update call to update all of the opp recs
            }
   }
        
}

 
This was selected as the best answer
Alaric WimerAlaric Wimer

Thank you! I've decided to make some slight changes like changing Map<String, String> to Map<Id, Id> and one more null pointer exception before making the single update call. Are there any issues with this final code?

trigger updateOppPoNumber on PO_Number__c (after insert) {
    Map<Id, Id> oppToPoNumMap = new Map<Id, Id>();
    for (PO_Number__c poNum : Trigger.new) {
        oppToPoNumMap.put(poNum.Opportunity__c, poNum.Id);
    }
    // Get Opportunities to update
    if (!oppToPoNumMap.isEmpty()) {
        List<Opportunity> oppsList = [
            SELECT Id, PO_Number__c
              FROM Opportunity
             WHERE Id IN :oppToPoNumMap.keySet()
        ];
        if (!oppsList.isEmpty()) {
            List<Opportunity> oppsToUpdate = new List<Opportunity>();
            for (Opportunity opp : oppsList) {
                opp.PO_Number__c = oppToPoNumMap.get(opp.Id);
                oppsToUpdate.add(opp);
            }
            if (!oppsToUpdate.isEmpty()) {
                update oppsToUpdate;
            }
        }
    }
}
 

 

 

Alaric WimerAlaric Wimer

One more revision, my final code:

 

trigger updateOppPoNumber on PO_Number__c (after insert) {
    Map<Id, Id> oppToPoNumMap = new Map<Id, Id>();
    for (PO_Number__c poNum : Trigger.new) {
        if (poNum.Id != null && poNum.Opportunity__c != null) {
            oppToPoNumMap.put(poNum.Opportunity__c, poNum.Id);
        }
    }
    // Get Opportunities to update
    if (!oppToPoNumMap.isEmpty()) {
        List<Opportunity> oppsList = [
            SELECT Id, PO_Number__c
              FROM Opportunity
             WHERE Id IN :oppToPoNumMap.keySet()
        ];
        if (!oppsList.isEmpty()) {
            List<Opportunity> oppsToUpdate = new List<Opportunity>();
            for (Opportunity opp : oppsList) {
                opp.PO_Number__c = oppToPoNumMap.get(opp.Id);
                oppsToUpdate.add(opp);
            }
            if (!oppsToUpdate.isEmpty()) {
                update oppsToUpdate;
            }
        }
    }
}