5

How do I update children of the same parent after insert of a new child?

For example, if I insert a new Contact with role of 'CEO' for an Account, then any Contact related to the Account with the role 'CEO' should be changed to 'Needs Review'. The code I have written works fine, but I need to bulkify it and follow SFDC's best practice to avoid SOQL queries inside FOR loops. I need help with revising my code.

I have changed my code to align with the example I outlined. The following is the trigger method class:

public without sharing class contactRoleChange {

// Get Contact records with Role = 'CEO' for the master Account
public static void checkRole(List<Contact> contacts){

    List<Contact> contactsToUpdate = new Contact[]{};
    integer count = 0;

    for (Contact currentContact : contacts) {
        ID Acct = currentContact.Account;
        if (currentContact.Role = 'CEO') {
            List<Contact> contactList = [SELECT Id, Role, Account
                                            FROM Contact
                                            WHERE (Contact.Role = 'CEO') AND
                                                (Contact.Account.ID = :Acct)
                                            ];
            for (Contact c : contactList) {
                contactsToUpdate.add(c);
                contactsToUpdate[count].Role = 'Needs Review';
                count ++;
            }
        }
    } 

    // Perform a single update DML statement outside the FOR loop
    update contactsToUpdate;

}// End checkRole     }// End class

2 Answers 2

6

One option to bulkify this it to gather up all the applicable AccountIds so they can be run in a single SOQL query. Use this collection to run the SOQL query once to get all the required Contacts in one go.

public without sharing class contactRoleChange {
    // Get Contact records with Role = 'CEO' for the master Account
    public static void checkRole(List<Contact> contacts){

        Set<Id> accountIds = new Set<Id>();
        Set<Id> sourceContactIds = new Set<Id>();
        for (Contact currentContact : contacts) {
            sourceContactIds.add(currentContact.Id);
            if (currentContact.Role == 'CEO') {
                accountIdToContactMap.add(currentContact.AccountId);
            }
        }

        List<Contact> contactsToUpdate = new List<Contact>();
        List<Contact> contactList = [SELECT Id
                                     FROM Contact
                                     WHERE (Contact.Role = 'CEO') AND
                                           (Contact.AccountID in :accountIdToContactMap)];
         for (Contact c : contactList) {

             // You could move this check up into the SOQL query to save bringing 
             // the records back. E.g. AND ID NOT IN :sourceContactIds
             if(sourceContactIds.contains(c.Id)) { continue; }

             contactsToUpdate.add(c);
             contactsToUpdate[count].Role = 'Needs Review';
         }

        // Perform a single update DML statement outside the FOR loop
        update contactsToUpdate;
    }
}
2
  • 1
    Wouldn't it be better to use a 'NOT IN :sourceContactIds' to help define the contactList?
    – Nick C
    Jun 27, 2014 at 1:30
  • @NickCook Yes, assuming the Contacts in question have been inserted. I have a comment above the continue that the check could be done in the SOQL query along that line. Jun 27, 2014 at 2:14
0

Below is the final code that was developed. Note the use of a Custom Label for the Pricing object's record type.

public without sharing class pricingInvalidateApproval {

// Get Pricing__c records with RecordType = 'NA Pricing Request' and Approval_Status__c = 'Final Approval' for the master Opportunity
public static void checkStatus(List<Pricing__c > pricings){

    // Create an unordered collection of Opportunities that does not contain any duplicates 
    Set<Id> opportunityIds = new Set<Id>();

    // Create an unordered collection of Pricing Requests that does not contain any duplicates 
    Set<Id> sourcePricingIds = new Set<Id>();
    for (Pricing__c currentPricing : pricings) {
        opportunityIds.add(currentPricing.Opportunity__c);
    }

    // Declare the record type for 'NA Pricing Request'
    String naPricingIdRT = System.Label.Pricing_NA_Record_Type_Id;

    // Query for Pricing Approval Request to update
    List<Pricing__c> pricingsToUpdate = new List<Pricing__c>();
    List<Pricing__c> pricingList = [SELECT Id, Approval_Status__c, Opportunity__c
                                    FROM Pricing__c
                                    WHERE (Pricing__c.Approval_Status__c = 'Final Approval') AND
                                    (Pricing__c.RecordTypeId = :naPricingIdRT) AND
                                    (Pricing__c.Opportunity__c IN :opportunityIds)                               
                                   ];
    integer count = 0;

    // Loop through each Pricing Approval Request and update Approval Status 
    for (Pricing__c p : pricingList) {
         pricingsToUpdate.add(p);
         pricingsToUpdate[count].Approval_Status__c = 'Invalidated by New Request';
        count++;
        System.debug('Pricing request count: ' + count);
    }

    // Perform a single update DML statement outside the FOR loop
    update pricingsToUpdate;

}// End checkStatus

}// End class

You must log in to answer this question.

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