Implementing Roll Up Summary Functionality For Objects Related Via Lookup Using Triggers

The requirement is to create a field on the Account object that displays the count of associated contact records for that specific Account, essentially implementing Roll-Up Summary functionality between Account and Contact.

This scenario is often encountered in interviews, and let’s break down the key points:

  1. Creation of Field:
    • A new field needs to be created on the Account object.
    • This field will represent the count of contact records related to the specific Account.
  2. Roll-Up Summary Logic:
    • When a new contact record is created, the count field on the parent Account should be updated.
    • If a contact is updated and its AccountId is modified to another Account, the count field on the original Account needs to be updated.
    • When a contact is deleted, the count field on the parent Account should be decremented.
    • If a deleted contact is undeleted, the count field on the parent Account should be incremented.
  3. Trigger Implementation:
    • The trigger is designed using the Trigger handler pattern.
    • The Trigger itself remains minimal, with the handler class containing the business logic.

This setup ensures efficient handling of the specified requirements, creating a robust solution for the given scenario.

trigger ContactTrigger on Contact(
  after insert,
  after update,
  after delete,
  after undelete
) {

  //when the record is inserted and undeleted increment count
  if (
    (Trigger.isAfter && Trigger.isInsert) ||
    (Trigger.isAfter && Trigger.isUndelete)
  ) {
    ContactHandler.incrementCount(Trigger.new);
  }
  //when updated and the parent is changed decrement it
  if ((Trigger.isAfter && Trigger.isUpdate)) {
    ContactHandler.decrementCount(Trigger.oldMap, Trigger.newMap);
  }
  //when deleted dercement it
  if (Trigger.isAfter && Trigger.isDelete) {
    ContactHandler.decrementDeleteCount(Trigger.oldMap);
  }

}
public with sharing class ContactHandler {
  public ContactHandler() {
  }

  public static void incrementCount(List<Contact> contactsList) {
    List<Account> tobeUpdatedAccountsMap = new List<Account>();
    List<Id> accountIds = new List<Id>();

    for (Contact c : contactsList) {
      accountIds.add(c.AccountId);
    }

    Map<Id, Account> accountsMap = new Map<Id, Account>(
      [SELECT Count__c FROM Account WHERE Id IN :accountIds]
    );

    for (Contact c : contactsList) {
      if (accountsMap.get(c.AccountId) != null) {
        Account a = new Account();
        a.Id = c.AccountId;
        System.debug(' 🚀 ' + c.Account.Count__c);
        a.count__c = accountsMap.get(c.AccountId).Count__c + 1;
        tobeUpdatedAccountsMap.add(a);
      }
    }

    update tobeUpdatedAccountsMap;
  }

  public static void decrementCount(
    Map<Id, Contact> oldContactsMap,
    Map<Id, Contact> newContactsMap
  ) {
    List<Id> accountIds = new List<Id>();

    for (Contact c : oldContactsMap.values()) {
      accountIds.add(c.AccountId);
    }
    for (Contact c : newContactsMap.values()) {
      accountIds.add(c.AccountId);
    }

    Map<Id, Account> accountsMap = new Map<Id, Account>(
      [SELECT Count__c FROM Account WHERE Id IN :accountIds]
    );

    List<Account> tobeUpdatedAccountsMap = new List<Account>();
    for (Contact c : newContactsMap.values()) {
      Id oldAccountId = oldContactsMap.get(c.Id).AccountId;
      Id newAccountId = newContactsMap.get(c.Id).AccountId;
      if (
        (oldAccountId != null && newAccountId != null) &&
        (oldAccountId != newAccountId)
      ) {
        Account a = new Account();
        a.Id = c.AccountId;
        a.count__c = accountsMap.get(c.AccountId).Count__c + 1;
        tobeUpdatedAccountsMap.add(a);
      }
    }

    for (Contact c : oldContactsMap.values()) {
      Id oldAccountId = oldContactsMap.get(c.Id).AccountId;
      Id newAccountId = newContactsMap.get(c.Id).AccountId;
      if (
        (oldAccountId != null && newAccountId != null) &&
        (oldAccountId != newAccountId)
      ) {
        Account a = new Account();
        a.Id = c.AccountId;
        a.count__c = accountsMap.get(c.AccountId).Count__c - 1;
        tobeUpdatedAccountsMap.add(a);
      }
    }
    update tobeUpdatedAccountsMap;
  }

  public static void decrementDeleteCount(Map<Id, Contact> oldContactsMap) {
    List<Id> accountIds = new List<Id>();

    for (Contact c : oldContactsMap.values()) {
      accountIds.add(c.AccountId);
    }

    Map<Id, Account> accountsMap = new Map<Id, Account>(
      [SELECT Count__c FROM Account WHERE Id IN :accountIds]
    );

    Map<Id, Account> tobeUpdatedAccountsMap = new Map<Id, Account>();
    for (Contact c : oldContactsMap.values()) {
      Id oldAccountId = oldContactsMap.get(c.Id).AccountId;
      if ((oldAccountId != null)) {
        Account a = new Account();
        a.Id = c.AccountId;
        a.count__c = accountsMap.get(c.AccountId).Count__c + 1;
        tobeUpdatedAccountsMap.put(a.Id, a);
      }
    }

    for (Contact c : oldContactsMap.values()) {
      Id oldAccountId = oldContactsMap.get(c.Id).AccountId;
      if ((oldAccountId != null)) {
        Account a = new Account();
        a.Id = c.AccountId;
        a.count__c = accountsMap.get(c.AccountId).Count__c - 1;
        tobeUpdatedAccountsMap.put(a.Id, a);
      }
    }
    update tobeUpdatedAccountsMap.values();
  }
}

Let me know if you have an improved or refactored approach for accomplishing this task!