Build the Trigger Handler
By the end of this lesson, you'll be able to:
- Write a thin trigger routing to a handler class, following Module 24's pattern
- Implement the handler's afterUpdate method for the basic case
- Explain why Trigger.oldMap is needed for this specific requirement
Prerequisites: "Design the Requirement"
The thin trigger
trigger OpportunityTrigger on Opportunity (after update) {
new OpportunityTriggerHandler().afterUpdate(Trigger.new, Trigger.oldMap);
}
trigger OpportunityTrigger on Opportunity (after update) {— exactly Module 24's "Avoiding Logic in Triggers" pattern.new OpportunityTriggerHandler().afterUpdate(Trigger.new, Trigger.oldMap);— the trigger itself does nothing but hand off to the handler, passing bothTrigger.newandTrigger.oldMapsince this requirement needs to compare old and new values.}— closes the trigger.
Detecting the actual change
public class OpportunityTriggerHandler {
public void afterUpdate(List<Opportunity> newOpportunities, Map<Id, Opportunity> oldMap) {
for (Opportunity opp : newOpportunities) {
Opportunity oldOpp = oldMap.get(opp.Id);
Boolean justWon = opp.StageName == 'Closed Won' && oldOpp.StageName != 'Closed Won';
Boolean justUnwon = opp.StageName != 'Closed Won' && oldOpp.StageName == 'Closed Won';
if (justWon) {
System.debug('Opportunity ' + opp.Id + ' just became Closed Won.');
}
if (justUnwon) {
System.debug('Opportunity ' + opp.Id + ' is no longer Closed Won.');
}
}
}
}
public class OpportunityTriggerHandler {— the handler class.public void afterUpdate(List<Opportunity> newOpportunities, Map<Id, Opportunity> oldMap) {— the method the trigger calls.for (Opportunity opp : newOpportunities) {— loops over the updated records.Opportunity oldOpp = oldMap.get(opp.Id);— this is Module 24'sTrigger.oldMaplesson doing real work — looks up this specific record's prior version.Boolean justWon = opp.StageName == 'Closed Won' && oldOpp.StageName != 'Closed Won';—justWondistinguishes "actually changed into Closed Won" from "was already Closed Won and something unrelated changed."Boolean justUnwon = opp.StageName != 'Closed Won' && oldOpp.StageName == 'Closed Won';—justUnwoncatches the reverse transition.- (blank line)
if (justWon) {— checks the first case.System.debug('Opportunity ' + opp.Id + ' just became Closed Won.');— logs it.}— closes theif.if (justUnwon) {— checks the second case.System.debug('Opportunity ' + opp.Id + ' is no longer Closed Won.');— logs it.}— closes theif.}— closes the loop.}— closes the method.}— closes the class.
Why this comparison matters
Without comparing against oldMap, every update on an Opportunity already sitting at Closed Won (say, just updating its description) would look identical to one that just became Closed Won — double-counting the same Amount into the rollup every time the record is touched. Catching only the actual transition is what keeps the rollup correct over time.
Exercise
Given opp and oldOpp, write a Boolean expression amountChanged that is true only when the Amount field's value actually changed.
Show hint
opp.Amount != oldOpp.Amount
Build the Trigger Handler Quiz
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 writes the trigger and handler skeleton, detecting exactly when an Opportunity newly becomes (or stops being) Closed Won.