Create A Trigger For A Roll-Up Summary To Calculate The Total Number Of Contacts Associated With An Account.

In this blog post, we’ll explore an illustration of how to update the total contact count on an Account using a roll-up summary trigger, such as an Apex trigger combined with an aggregate query. This involves updating the child record count on a parent record in a lookup relationship, utilizing an approach that involves aggregate queries and maps.

Scenario:

Consider a sales team utilizing Salesforce to monitor accounts and contacts. When activities such as creating, adding, removing, linking, or deleting a Contact from an Account occur, it becomes necessary to update the count of Contacts associated with that Account, ensuring an accurate representation. This trigger streamlines the process, enabling the sales team to maintain an up-to-date count of Contacts for each Account without manual intervention.

Below is an example of a roll-up summary Apex trigger that updates the “Number of Contacts” field on the parent Account object. This trigger is triggered when someone performs operations such as inserting, deleting, updating, or undeleting a Contact.

Trigger Explanation :

Here is a step-by-step breakdown of the code’s functionality:

  1. Initialization of a set to store the Ids of the parent object Account.
  2. During insert or undelete triggers, the code examines the parent Account Ids of new Contacts for null values. If any are found to be not null, the corresponding Account Ids are added to the set created in step 1.
  3. For insert or undelete events, the code iterates through new Contacts to ensure their parent Account Ids are not null.
  4. In the case of update events, the trigger checks for modifications to parent Account Ids by iterating through new Contacts. If non-null Account Ids are identified, they are added to the set created in step 1.
  5. Declaration of a map to store the count of Contacts for each parent Account.
  6. An aggregate query retrieves the count of Contacts associated with each parent Account from the set established in step 1. The code then stores the contact count for each parent Account in the map created in step 5.
  7. Creation of a list to store parent Account records for updating.
  8. Iteration through the parent Account records and setting the “Number_of_Contacts__c” field to the count of Contacts stored in the map. If the count is not available in the map, it is set to 0. After updating the count in the map from step 5, the parent Account record is added to the list established in step 7.
  9. Updating the parent Account records in the list defined in step 7.

Sample Code :

trigger UpdateContactCountOnAccount on Contact(after delete, after insert, after update, after undelete) {
    // Declare a set to store the Ids of the parent object Account
    Set < Id > accountIds = new Set < Id > ();
    // Check if the trigger is for insert or undelete
    if (Trigger.isInsert || Trigger.isUndelete) {
        for (Contact child: Trigger.new) {
            // Check if the parent account Id is not null
            if (child.AccountId != null) {
                accountIds.add(child.AccountId);
            }
        }
    }
    // Check if the trigger is for delete
    if (Trigger.isDelete) {
        for (Contact child: Trigger.old) {
            // Add the parent account Id of each deleted child record to the set of parent Ids to be updated.
            accountIds.add(child.AccountId);
        }
    }
    // Check if the trigger is for update
    if (Trigger.isUpdate) {
        for (Contact child: Trigger.new) {
            // Check if the parent Id is changed
            if (child.AccountId != Trigger.oldMap.get(child.Id).AccountId) {
                // Check if either the old parent Id or the value field of the child record has been changed 
                // and if so, it adds the old parent Id to the set of parent Ids to be updated.
                if (Trigger.oldMap.get(child.Id).AccountId != null) {
                    accountIds.add(Trigger.oldMap.get(child.Id).AccountId);
                }
                // Check if the new parent account Id is not null
                if (child.AccountId != null) {
                    // Add the parent account Id of each updated contact record to the set of parent account Ids
                    accountIds.add(child.AccountId);
                }
            }
        }
    }
    if (!accountIds.isEmpty()) {
        // Declare a map to store the number of contacts for each parent 
        Map < Id, Integer > contactsCount = new Map < Id, Integer > ();
        // Increment the contactsCoun for each parent
        for (AggregateResult ar: [SELECT AccountId, COUNT(Id) contactCount FROM Contact WHERE AccountId IN: accountIds GROUP BY AccountId]) {
            contactsCount.put((Id) ar.get('AccountId'), (Integer) ar.get('contactCount'));
        }
        // Create a list of parent records to update
        List < Account > parentsToUpdate = new List < Account > ();
        // Loop through the parent records to update the number of contacts field
        for (Account parent: [SELECT Id, Number_of_Contacts__c FROM Account WHERE Id IN: accountIds]) {
            if (contactsCount.containsKey(parent.Id)) {
                parent.Number_of_Contacts__c = contactsCount.get(parent.Id);
            } else {
                parent.Number_of_Contacts__c = 0;
            }
            parentsToUpdate.add(parent);
        }
        // Update the parent object
        if (!parentsToUpdate.isEmpty()) {
            update parentsToUpdate;
        }
    }
}

Note: This trigger can be applied to any objects featuring a lookup relationship between the parent and child.