Intermediate 30 min read

Enforce Business Rules

By the end of this lesson, you'll be able to:

  • Implement the date-range overlap check from Lesson 1's formula
  • Compare each new request against the pre-fetched existing requests
  • Block an overlapping request with a clear addError() message

Prerequisites: "Build a Bulk-Safe Trigger"

The overlap check as code

private Boolean datesOverlap(Date startA, Date endA, Date startB, Date endB) {
    return startA <= endB && startB <= endA;
}
  1. private Boolean datesOverlap(Date startA, Date endA, Date startB, Date endB) { — a small, well-named private helper — this is Module 12's habit of extracting a clearly-named private method, applied from the very start rather than as an afterthought.
  2. return startA <= endB && startB <= endA; — a direct translation of Lesson 1's formula.
  3. } — closes the method.

Checking each new request against existing ones

public void beforeInsert(List<Leave_Request__c> newRequests) {
    Set<String> employeeNames = new Set<String>();
    for (Leave_Request__c req : newRequests) {
        employeeNames.add(req.Employee_Name__c);
    }

    List<Leave_Request__c> existingRequests = [
        SELECT Id, Employee_Name__c, Start_Date__c, End_Date__c
        FROM Leave_Request__c
        WHERE Employee_Name__c IN :employeeNames AND Status__c != 'Rejected'
    ];

    for (Leave_Request__c newReq : newRequests) {
        for (Leave_Request__c existing : existingRequests) {
            Boolean sameEmployee = newReq.Employee_Name__c == existing.Employee_Name__c;
            Boolean overlaps = datesOverlap(newReq.Start_Date__c, newReq.End_Date__c, existing.Start_Date__c, existing.End_Date__c);

            if (sameEmployee && overlaps) {
                newReq.Start_Date__c.addError('This leave request overlaps with an existing request.');
            }
        }
    }
}
  1. public void beforeInsert(List<Leave_Request__c> newRequests) { — the method.
  2. Set<String> employeeNames = new Set<String>(); — collects unique employee names.
  3. for (Leave_Request__c req : newRequests) { — loops over the new requests.
  4. employeeNames.add(req.Employee_Name__c); — adds each one.
  5. } — closes the loop.
  6. (blank line)
  7. List<Leave_Request__c> existingRequests = [ — the one upfront query from the previous lesson.
  8. SELECT Id, Employee_Name__c, Start_Date__c, End_Date__c — the fields needed.
  9. FROM Leave_Request__c — the object.
  10. WHERE Employee_Name__c IN :employeeNames AND Status__c != 'Rejected' — every relevant existing request.
  11. ]; — closes the query.
  12. (blank line)
  13. for (Leave_Request__c newReq : newRequests) { — the outer loop, over new requests.
  14. for (Leave_Request__c existing : existingRequests) { — the inner loop, over existing requests — two nested loops, both purely in-memory (Module 25's key requirement), no SOQL or DML inside either one.
  15. Boolean sameEmployee = newReq.Employee_Name__c == existing.Employee_Name__c; — checks whether the two records are even comparable.
  16. Boolean overlaps = datesOverlap(newReq.Start_Date__c, newReq.End_Date__c, existing.Start_Date__c, existing.End_Date__c); — the date-range check.
  17. (blank line)
  18. if (sameEmployee && overlaps) { — both conditions must be true.
  19. newReq.Start_Date__c.addError('This leave request overlaps with an existing request.'); — blocks any genuine overlap with a clear message.
  20. } — closes the if.
  21. } — closes the inner loop.
  22. } — closes the outer loop.
  23. } — closes the method.

Why the nested loop is still bulk-safe

Nested loops can look alarming, but bulk-safety is specifically about SOQL queries and DML statements, not about loop nesting itself — Module 25 never said "avoid nested loops," it said "avoid SOQL/DML inside a loop." This nested comparison runs entirely against data already fully loaded in memory (newRequests and existingRequests), so it costs zero additional queries or DML statements no matter how many times it iterates.

Exercise

Write the datesOverlap helper method as a comment-free, standalone method, following the formula from Lesson 1.

Show hint

return startA <= endB && startB <= endA;

APEX

Enforce Business Rules Quiz

1. Is a nested for loop over in-memory Lists a governor-limit risk?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

This lesson implements the actual overlap rule, comparing each new request against every existing request already fetched for that same employee.