10 Practice Problems for APEX Triggers: A Beginner’s Guide

10 Practice Problems for APEX Triggers: A Beginner’s Guide

On February 14, 2025, Posted by , In Apex,Salesforce, With Comments Off on 10 Practice Problems for APEX Triggers: A Beginner’s Guide
10 Practice Problems for APEX Triggers A Beginner’s Guide

Apex triggers are essential tools in Salesforce development, allowing you to automate processes and create custom behaviors. Whether you’re a fresher or an experienced developer, practicing Apex trigger problems can significantly boost your understanding. In this post, I’ll walk you through 7 practice problems for Apex triggers that can help beginners get started and refine their skills. These problems are widely recognized and effective for learning.

Enhance your expertise with Salesforce online training in Admin, Developer, and AI modules. Gain hands-on experience through real-world projects to master Salesforce and accelerate your career.

Problem 1: Update Contact_Created__c Checkbox on Account

Pre-Req:

Create a checkbox field on Account called Contact_Created__c, default to off.

Assignment:

Create an Apex trigger on the Contact object that updates the Contact_Created__c checkbox field to TRUE when a Contact is created and associated with an Account.

Solution:

trigger AccountForContact on Contact (after insert, before update) {
    if (Trigger.isInsert && Trigger.isAfter) {
        Map<Id, Account> accountMap = new Map<Id, Account>();

        // Collect Account IDs from newly inserted Contacts
        for (Contact con : Trigger.New) {
            if (con.AccountId != null) {
                accountMap.put(con.AccountId, null);
            }
        }

        // Query relevant Accounts and store in Map
        if (!accountMap.isEmpty()) {
            for (Account acc : [SELECT Id, Contact_Created__c FROM Account WHERE Id IN :accountMap.keySet()]) {
                acc.Contact_Created__c = true;
                accountMap.put(acc.Id, acc);
            }
        }

        // Update Accounts in a single DML operation
        if (!accountMap.isEmpty()) {
            update accountMap.values();
        }
    }
}

Explanation:

This Apex trigger runs after a Contact is inserted and updates the related Account’s Contact_Created__c field. It first collects unique AccountId values from newly inserted Contacts and queries the corresponding Accounts using a Map<Id, Account>. The trigger then updates the Contact_Created__c field to true for these Accounts and stores the changes in the map. Finally, it performs a single bulk update using update accountMap.values();, ensuring efficiency and avoiding governor limits. This optimized approach enhances performance, scalability, and maintains best practices for handling large data volumes.

Problem 2: Mark Out_of_Zip Checkbox on Account Based on Postal Code

Pre-Req:

Create a checkbox field on Account called Out_of_Zip, default to off.

Assignment:

When an Account’s Billing Address is modified, check if any of the Contacts associated with that Account have a mailing postal code that is different from the Account’s Billing Postal Code. If so, mark the Out_of_Zip checkbox to TRUE.

Solution:

trigger TriggerExample on Account (before update, before insert) {
    Set<Id> changedAccounts = new Set<Id>();

    for (Account newAcc : Trigger.New) {
        Account oldAcc = Trigger.OldMap != null ? Trigger.OldMap.get(newAcc.Id) : null;
        
        if (oldAcc != null && (newAcc.BillingStreet != oldAcc.BillingStreet || 
            newAcc.BillingCity != oldAcc.BillingCity ||
            newAcc.BillingState != oldAcc.BillingState ||
            newAcc.BillingPostalCode != oldAcc.BillingPostalCode ||
            newAcc.BillingCountry != oldAcc.BillingCountry)) {
            changedAccounts.add(newAcc.Id);
        }
    }

    if (!changedAccounts.isEmpty()) {
        Map<Id, Boolean> accountsOutOfZip = new Map<Id, Boolean>();
        
        for (Contact c : [SELECT AccountId, MailingPostalCode, Account.BillingPostalCode 
                          FROM Contact WHERE AccountId IN :changedAccounts]) {
            if (c.MailingPostalCode != c.Account.BillingPostalCode) {
                accountsOutOfZip.put(c.AccountId, true);
            }
        }

        for (Account acc : Trigger.New) {
            acc.Out_of_Zip__c = accountsOutOfZip.containsKey(acc.Id);
        }
    }
}

Explanation:

This Apex trigger runs before inserting or updating an Account and checks if the Billing Address fields have changed. It first compares Trigger.New with Trigger.OldMap (ensuring null safety) and adds modified Account IDs to a Set<Id>. Next, it queries related Contacts whose MailingPostalCode differs from their Account’s BillingPostalCode and stores results in a Map<Id, Boolean>. The trigger then efficiently updates the Out_of_Zip__c field for Accounts in a single iteration based on whether any associated Contacts are outside the Account’s ZIP code. This approach ensures better bulk processing, governor limit optimization, and improved maintainability.

Problem 3: Create Default Contact When a New Account is Created

Pre-Req:

Create a checkbox field on Account called Only_Default_Contact, default to off.

Assignment:

When a new Account is created, create a new Contact with specific details. If there are more than one Contact associated with the Account, update the Only_Default_Contact checkbox to FALSE.

Solution:

trigger defaultContact on Account (after insert) {
    Set<Id> accountIds = new Set<Id>();
    
    for (Account acc : Trigger.New) {
        accountIds.add(acc.Id);
    }
    
    List<Account> accountsToUpdate = [SELECT Id, Only_Default_Contact FROM Account WHERE Id IN :accountIds];
    
    List<Contact> contactsToInsert = new List<Contact>();
    for (Account acc : accountsToUpdate) {
        Contact con = new Contact();
        con.FirstName = 'Info';
        con.LastName = 'Default';
        con.Email = 'info@websitedomain.tld';
        con.AccountId = acc.Id;
        contactsToInsert.add(con);

        // Mark Only_Default_Contact to true
        acc.Only_Default_Contact = true;
    }
    
    if (!contactsToInsert.isEmpty()) {
        insert contactsToInsert;
    }
    
    if (!accountsToUpdate.isEmpty()) {
        update accountsToUpdate;
    }
}

Explanation:

This trigger runs after an Account is inserted and creates a default Contact for each newly inserted Account. It first collects the AccountId values from Trigger.New into a Set<Id> to query the related Accounts. It then prepares a list of Contacts to insert, setting default fields like FirstName, LastName, and Email. For each Account, the trigger also sets the Only_Default_Contact field to true. After preparing both the Contact and Account updates, it performs bulk-safe insert and update operations to handle multiple records efficiently.

Problem 4: Create an Account Record When a Contact is Created Without an Account

Assignment:

Create an Apex trigger that creates an Account record whenever a new Contact is created without an associated Account.

Solution:

trigger ContactCustomTriggerExample on Contact (after insert) {
    List<Account> accListToInsert = new List<Account>();
    
    for (Contact con : Trigger.New) {
        // Only create a new Account if AccountId is null on Contact
        if (con.AccountId == null) {
            Account acc = new Account();
            acc.Name = con.LastName;
            acc.Phone = con.Phone;
            accListToInsert.add(acc);
        }
    }
    
    // Insert Accounts if there are any to insert
    if (!accListToInsert.isEmpty()) {
        insert accListToInsert;
    }
}

Explanation:

This trigger fires after a Contact is inserted and checks if any of the Contacts do not have an associated AccountId. For Contacts without an Account, a new Account is created, with fields like Name (set to Contact’s LastName) and Phone. These new Accounts are added to a list, and if any Accounts are collected, they are inserted in bulk with a single DML operation. The approach ensures that only Contacts missing an Account trigger the creation of a new Account, optimizing performance.

Problem 5: Mark need_intel Checkbox on Account Based on Dead Contacts

Pre-Req:

Create a checkbox field on Account called need_intel, default to off, and a checkbox field on Contact called Dead, default to off.

Assignment:

If 70% or more of the Contacts on an Account are marked as Dead, mark the need_intel field on the Account to TRUE.

Solution:

public class ContactHandler {
    public static void DeadIntel(List<Contact> cont) {
        Set<Id> accIds = new Set<Id>();
        
        for (Contact con : cont) {
            if (con.AccountId != null) {
                accIds.add(con.AccountId);
            }
        }

        Map<Id, List<Contact>> accContactMap = new Map<Id, List<Contact>>();
        List<Account> accUpdateList = new List<Account>();
        
        for (Contact con : [SELECT AccountId, Dead__c FROM Contact WHERE AccountId IN :accIds]) {
            List<Contact> contacts = accContactMap.get(con.AccountId);
            if (contacts == null) {
                contacts = new List<Contact>();
            }
            contacts.add(con);
            accContactMap.put(con.AccountId, contacts);
        }

        for (Id accId : accContactMap.keySet()) {
            Integer deadCount = 0;
            Integer totalContacts = accContactMap.get(accId).size();
            for (Contact con : accContactMap.get(accId)) {
                if (con.Dead__c) {
                    deadCount++;
                }
            }
            if ((deadCount * 100) / totalContacts >= 70) {
                accUpdateList.add(new Account(Id = accId, needintel__c = true));
            }
        }

        if (!accUpdateList.isEmpty()) {
            update accUpdateList;
        }
    }
}

trigger UpdateContact on Contact (after update) {
    if (Trigger.isUpdate && Trigger.isAfter) {
        ContactHandler.DeadIntel(Trigger.New);
    }
}

Explanation:

This code tracks dead Contacts in a set of Accounts and marks the associated Account’s needintel__c field if more than 70% of its Contacts are marked as dead. The trigger runs after a Contact is updated and calls the DeadIntel method, passing the updated Contacts to it. The method first collects Account IDs from the Contacts and queries the relevant Contacts to group them by AccountId. It calculates the percentage of dead Contacts and updates the needintel__c field on the Account if the threshold is met. The code efficiently handles bulk processing and minimizes DML operations by using maps and lists.

Problem 6: Auto-Assign Case Owner

Pre-requisites:

Understanding of before insert triggers.
Knowledge of Case Object and OwnerId field.
A predefined Queue in Salesforce (e.g., “High Priority Cases”).

Assignment:

Write a before insert trigger on the Case object.
If a new Case’s Priority is ‘High’, set its OwnerId to a specific Queue Id.

Solution:

We check the Case Priority before inserting and assign it to a predefined Queue.

Code:

trigger AutoAssignCaseOwner on Case (before insert) {
    Id queueId = [SELECT Id FROM Group WHERE Name = 'High Priority Cases' AND Type = 'Queue' LIMIT 1].Id;
    for (Case c : Trigger.new) {
        if (c.Priority == 'High') {
            c.OwnerId = queueId;
        }
    }
}

Explanation:

The code first retrieves the Queue Id for “High Priority Cases” using a SOQL query. It then iterates over Trigger.new and checks if the Priority of the Case is ‘High’. If it is, the Case’s OwnerId is updated with the Queue Id. This ensures that all high-priority cases are automatically assigned to the correct support team without manual intervention.

Problem 7: Update Profile__c Field on Contact When Account Website is Updated

Pre-Req:

Create a text field on Contact called Profile, 255 characters.

Assignment:

When an Account’s Website field is updated, update all related Contacts’ Profile__c field to contain the Account’s website and the first letter of the Contact’s first name, followed by their last name.

Solution:

trigger TriggerExample3 on Account (after update) {
    if (Trigger.isAfter && Trigger.isUpdate) {
        Set<Id> accountIdSet = new Set<Id>();
        
        // Collect Account IDs where Website is not null
        for (Account ac : Trigger.New) {
            if (ac.Website != null) {
                accountIdSet.add(ac.Id);
            }
        }

        // Query Contacts linked to the Accounts with a Website
        List<Contact> contactList = [SELECT Id, FirstName, LastName, Profile__c, AccountId, Account.Website FROM Contact WHERE AccountId IN :accountIdSet];
        
        // Update Profile__c with a URL-like value based on the Account Website and Contact Name
        for (Contact con : contactList) {
            if (con.FirstName != null) {
                con.Profile__c = con.Account.Website + '/' + con.FirstName.substring(0, 1) + con.LastName;
            }
        }

        // Update Contacts in bulk
        if (!contactList.isEmpty()) {
            update contactList;
        }
    }
}

Explanation:

This trigger runs after an Account update and checks for Accounts with a non-null Website. For such Accounts, it queries their related Contacts and updates the Profile__c field based on the Account.Website and the Contact’s name. Specifically, it creates a URL-like string using the first letter of the FirstName and the full LastName of the Contact. The trigger performs a bulk update on Contacts to avoid hitting DML limits, ensuring optimal performance.

Problem 8: Count Contacts for Each Account

Pre-requisites:

Understanding of after insert & after delete triggers.
Knowledge of Aggregate SOQL for counting records.
A custom field ‘Number_of_Contacts__c’ on the Account object to store contact count.

Assignment:

Create an after insert & after delete trigger on Contact.
When a Contact is added or deleted, update the Account’s ‘Number_of_Contacts__c’ field.

Solution:

We recalculate the Contact count each time a Contact is inserted or deleted and update the related Account.

Code:

trigger UpdateContactCount on Contact (after insert, after delete) {
    Set<Id> accountIds = new Set<Id>();
    
    if (Trigger.isInsert || Trigger.isDelete) {
        for (Contact c : Trigger.new != null ? Trigger.new : Trigger.old) {
            if (c.AccountId != null) {
                accountIds.add(c.AccountId);
            }
        }
    }
    
    if (!accountIds.isEmpty()) {
        Map<Id, Integer> contactCounts = new Map<Id, Integer>();
        for (AggregateResult ar : [SELECT AccountId, COUNT(Id) count FROM Contact WHERE AccountId IN :accountIds GROUP BY AccountId]) {
            contactCounts.put((Id)ar.get('AccountId'), (Integer)ar.get('count'));
        }
        
        List<Account> accountsToUpdate = new List<Account>();
        for (Id accId : accountIds) {
            accountsToUpdate.add(new Account(Id = accId, Number_of_Contacts__c = contactCounts.get(accId) != null ? contactCounts.get(accId) : 0));
        }
        update accountsToUpdate;
    }
}

Explanation:

This trigger first collects all Account Ids from the Contacts being inserted or deleted. It then runs an Aggregate SOQL query to count the number of Contacts per Account. A map stores these counts, which are used to update the Number_of_Contacts__c field on the Account. Finally, all affected Accounts are updated in one batch operation. This ensures that the Contact count for each Account remains accurate and up-to-date.

Problem 9: Prevent Account Deletion

Pre-requisites:

Basic understanding of Apex Triggers and Trigger Context Variables.
Knowledge of SOQL queries to check related records.
A Salesforce Developer Org to implement and test the trigger.

Assignment:

Create a before delete trigger on the Account object.
If an Account has related Contacts, prevent deletion and display an error message.

Solution:

We check for Contacts linked to the Account before deletion. If Contacts exist, we add an error message to prevent the deletion.

Code

trigger PreventAccountDeletion on Account (before delete) {
    for (Account acc : Trigger.old) {
        Integer contactCount = [SELECT COUNT() FROM Contact WHERE AccountId = :acc.Id];
        if (contactCount > 0) {
            acc.addError('You cannot delete this Account because it has related Contacts.');
        }
    }
}

Explanation:

This trigger runs before an Account is deleted. It loops through Trigger.old, which holds the records being deleted, and queries the number of Contacts linked to each Account. If an Account has related Contacts, addError() prevents deletion. This ensures data integrity by preventing accidental deletion of Accounts with active Contacts.

Problem 10: Mark is_gold Checkbox on Account Based on Opportunity Amount

Pre-Req:

Create a checkbox field on Account called is_gold, default to off.

Assignment:

If an Opportunity related to an Account has an amount greater than $20,000, mark the is_gold checkbox on the Account as TRUE.

Solution:

trigger TriggerExample4 on Account (after update) {
    Integer amount = 20000;
    List<Account> accountsToUpdate = new List<Account>();

    for (Account acc : Trigger.New) {
        // Query Opportunities where Amount > 20000 for the related Account
        List<Opportunity> opps = [SELECT Id FROM Opportunity WHERE AccountId = :acc.Id AND Amount > :amount];
        
        // Set is_gold__c field based on the presence of qualifying Opportunities
        acc.is_gold__c = (opps.size() > 0);
        accountsToUpdate.add(acc);
    }

    // Update all modified Accounts in bulk to avoid multiple DML statements
    if (!accountsToUpdate.isEmpty()) {
        update accountsToUpdate;
    }
}

Explanation:

This trigger executes after an Account is updated and checks whether the related Opportunities exceed a specified amount (in this case, 20,000). It queries the Opportunities associated with each Account and sets the is_gold__c field to true if any Opportunity qualifies. The trigger collects all updated Accounts in a list to perform a single bulk update, reducing DML operations. The use of opps.size() > 0 ensures that only Accounts with qualifying Opportunities are marked as “gold.”

Conclusion

These 7 practice problems cover a variety of scenarios for Apex triggers in Salesforce, ranging from creating records automatically to updating fields based on certain conditions. Whether you’re just starting with Apex or looking to refine your skills, these problems will give you valuable experience in trigger development. If you have any questions, feel free to ask or dive into these examples using the Developer Console for hands-on practice!

Accelerate your career with Salesforce training in Hyderabad.

Unlock new opportunities with Salesforce training in Hyderabad, by advancing your skills in Admin, Developer, and AI modules. Our expert-led program combines theoretical knowledge with hands-on projects, ensuring a deep understanding of Salesforce concepts. Learn through real-world case studies and industry-driven assignments to sharpen your technical expertise and problem-solving abilities.

Gain an edge in the job market with personalized mentorship, interview coaching, and certification preparation. Equipped with practical exercises and comprehensive study materials, you’ll be ready to tackle complex business challenges using Salesforce.

Don’t wait—join our free demo session today and begin your journey to a successful Salesforce career in Hyderabad!

Comments are closed.