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
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;
    }
}
| improve this answer | |
  • 1
    Wouldn't it be better to use a 'NOT IN :sourceContactIds' to help define the contactList? – Nick Cook Jun 27 '14 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. – Daniel Ballinger Jun 27 '14 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
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.