Learning Apex Triggers (Part 3) – Comprehensive Guide to Before and After Trigger Scenarios

Learning Apex Triggers (Part 3) – Comprehensive Guide to Before and After Trigger Scenarios

, ,

In continuation of our previous post Learning Apex Triggers (Part1), this guide will provide use cases. It will also provide examples for each trigger event. It is part of the “Learning Apex Triggers” series.

Salesforce triggers are small pieces of code that run automatically when records change in Salesforce. They let you add custom logic before or after data is inserted, updated, deleted, or even restored (undeleted). Triggers can set default values, enforce business rules, update related records, or send notifications based on your company’s needs.

In Salesforce there are below seven trigger events-

Before Insert Trigger

  • When to use: Setting or checking fields on a new record before it’s saved. This is good for default values or initial validation.
  • Key idea: You get access to the new records via Trigger.new and can change their fields.

Examples Use Cases:

  • If a new Opportunity is created without a Stage, set a default Stage (like Prospecting).
  • When a Lead is inserted, require that an email address is provided (or fill a default placeholder).
  • Setting the initial Case Status to “New” if it is blank on creation.
trigger SetDefaultStage on Opportunity (before insert) {
    for (Opportunity opp : Trigger.new) {
        // If Stage is not set on a new opportunity, default it to 'Prospecting'
        if (opp.StageName == null) {
            opp.StageName = 'Prospecting';
        }
    }
}

In this code, before any new Opportunity is saved, we check if its StageName is empty. If it is, we set it to “Prospecting.” Because this is a before insert trigger, the change is made before the record is written to the database.

Before Update Trigger

  • When to use: Checking or fixing data just before a record is updated. Good for enforcing rules or automatically setting fields on update.
  • Key idea: You have access to Trigger.new (the new values) and Trigger.old/Trigger.oldMap (the old values) to compare changes.

Examples Use Cases:

  • If an Opportunity is changed to Stage “Closed Won”, check if the Close Date is empty. If it is, set the Close Date to today’s date.
  • Do not allow discounts to be set too high. If someone tries to update the Opportunity Amount to over $100,000, show an error and block the update.
  • When a Contact’s email is updated, trim extra spaces or enforce a proper format before saving.

Code Example:

trigger SetCloseDate on Opportunity (before update) {
    for (Opportunity opp : Trigger.new) {
        // If stage is changed to Closed Won and CloseDate is blank, set it to today
        if (opp.StageName == 'Closed Won' && opp.CloseDate == null) {
            opp.CloseDate = Date.today();
        }
    }
}

In this before update trigger, we check each updated Opportunity. If its stage is updated to “Closed Won”, we set the Close Date to today if it doesn’t have one. This action happens automatically. This change happens before saving the updated record.

Before Delete Trigger

  • When to use: Validating deletion or stopping it if business rules require.
  • Key idea: You access the records being deleted via Trigger.old (because they no longer have new values) and can call addError() to block the delete.

Examples Use Cases:

  • Prevent deleting an Opportunity if its Amount is over $100,000.
  • Do not allow the deletion of an Account if it has open Cases. Avoid deletion if it has important data, such as when a custom “Critical_Account” field is true.
  • Check if a Case with status “Escalated” is about to be deleted, and stop it to protect critical issues.

Code Example:

trigger PreventHighValueDeletion on Opportunity (before delete) {
    for (Opportunity opp : Trigger.old) {
        // Prevent deleting an Opportunity worth more than $100,000
        if (opp.Amount != null && opp.Amount > 100000) {
            opp.addError('Cannot delete high-value opportunities.');
        }
    }
}

Here, we loop over each Opportunity being deleted (Trigger.old) and if its Amount is greater than 100,000, we call addError(). This stops the deletion and shows an error message to the user.

After Insert Trigger

  • When to use: Performing actions that need the new record’s ID or have side effects.
  • Key idea: The record is already saved. Use this to create related records, send emails, or update other objects. Access the new records via Trigger.new.

Examples Use Cases:

  • After a new Account is created, automatically create a welcome Task or Contact associated with that Account.
  • When a new Lead is inserted, send a notification email to the sales rep or assign it to a queue.
  • After inserting a new Opportunity, update inventory levels by subtracting the ordered quantity from a product stock record.

Code Example:

trigger CreateFollowUpTask on Opportunity (after insert) {
    List<Task> tasksToAdd = new List<Task>();
    for (Opportunity opp : Trigger.new) {
        Task t = new Task();
        t.WhatId = opp.Id;  // relates task to the opportunity
        t.Subject = 'Follow up on opportunity';
        t.Status = 'Not Started';
        tasksToAdd.add(t);
    }
    if (!tasksToAdd.isEmpty()) {
        insert tasksToAdd;
    }
}

This after insert trigger creates a new Task for each newly inserted Opportunity. Because it runs after insert, each Opportunity has an Id we can use as WhatId on the Task. We collect all tasks in a list and insert them at once.

After Update Trigger

  • When to use: Making updates to other records after a save, or responding to changes.
  • Key idea: The updated record already exists. Use this to touch related objects or log changes. You can compare Trigger.new and Trigger.old to detect specific changes.

Examples Use Cases:

  • If an Opportunity stage changes to “Closed Won,” update the parent Account’s total closed deals. Alternatively, add the amount to a summary field.
  • When a Case status is changed to “Closed,” create a follow-up Task or send a closure email to the customer.
  • After updating a Product’s price, recalculate the total for all related Orders or Opportunities.

Code Example:

trigger NotifyOnCaseClose on Case (after update) {
    for (Case c : Trigger.new) {
        // If a Case is now Closed (status changed), create a completion task
        if (c.Status == 'Closed') {
            Task t = new Task(
                WhatId = c.Id,
                Subject = 'Case closed',
                Status = 'Completed'
            );
            insert t;
        }
    }
}

In this after update trigger, for each Case that is now closed, we insert a new completed Task. This could be used to notify someone or record that the case was closed. (In a real scenario, you might check Trigger.oldMap to only do this when the status changed to Closed, but this simple example shows the basic idea.)

After Delete Trigger

  • When to use: Cleaning up or updating other data after a delete.
  • Key idea: Use Trigger.old to know what was deleted. You might delete related child records or update summary fields on parent records.

Examples Use Cases:

  • If an Account is deleted, delete any related Contacts or custom child records that Salesforce didn’t automatically remove.
  • After a Campaign is deleted, update a dashboard or notify marketing managers that it was removed.
  • If an Inventory record is deleted, set a flag on a related Product or log the deletion for auditing.

Code Example:

trigger DeleteRelatedContacts on Account (after delete) {
    // Find all contacts whose Account was deleted
    List<Contact> relatedContacts = [SELECT Id FROM Contact WHERE AccountId IN :Trigger.oldMap.keySet()];
    if (!relatedContacts.isEmpty()) {
        delete relatedContacts;
    }
}

This after delete trigger looks up Contacts whose AccountId was in the set of deleted Accounts (Trigger.oldMap.keySet()). It then deletes those Contacts. (In many orgs, Contacts are automatically deleted when an Account is deleted, but this example shows how to clean up related data if needed.)

After Undelete Trigger

  • When to use: Adjusting data or notifying users when a record is brought back.
  • Key idea: Treat the record as “new again,” but keep in mind it had past data. Use Trigger.new to see the restored records.

Examples Use Cases:

  • If a Contact is undeleted, add the Contact back to an email campaign or reassign it to a user.
  • When an Account is undeleted, set a custom “Status” field back to “Active” or notify the record owner.
  • If a Custom Object record is undeleted, rebuild any related lookup or summary fields that might have been cleared.

Code Example:

trigger ReactivateTasksOnContactUndelete on Contact (after undelete) {
    List<Task> tasksToAdd = new List<Task>();
    for (Contact c : Trigger.new) {
        Task t = new Task(
            WhatId = c.Id,
            Subject = 'Contact was restored',
            Status = 'Not Started'
        );
        tasksToAdd.add(t);
    }
    if (!tasksToAdd.isEmpty()) {
        insert tasksToAdd;
    }
}

This after undelete trigger creates a new Task for each restored Contact. It might be used to notify someone that the Contact record was recovered. Because the trigger is after undelete, the Contact records are already active again and have their IDs.

One response to “Learning Apex Triggers (Part 3) – Comprehensive Guide to Before and After Trigger Scenarios”

  1. […] You can refer to our previous blog for more detail understanding on flow – Learning Apex Triggers (Part 1) – Understanding Trigger Basics & Learning Apex Triggers (Part 3) – Comprehensive Guide to Before and After Trigger Scenarios […]

    Like

Leave a comment