BeforeUpdate
Runs in before update, after the user's changes and before they are saved. Compare the old and new values, then set fields with a Populator or reject the change with a Validator.
Roles
- Populator: set, derive or clear fields, usually when another field changed.
- Validator: reject a change with an error message.
Add-ons
- ParentQuery: load parent (lookup) fields before the first handler runs; read them with
getNewParent. - PriorParentQuery: load the parent the old row pointed to; read it with
getOldParent. - RelatedQuery: query children, siblings or any other records once per chunk; read them with
getRelated. - Bypassable: skip this handler for the whole chunk when a condition holds.
- RecursionGuard: cap how many times this handler acts on the same record in one transaction (default 3). Populator only.
- Finalizer: run once after this handler’s records, with the records that qualified.
- ContinueOnError: log and swallow this handler’s exceptions, so the save goes on.
Register
Implement TriggerOrchestrator.BeforeUpdate on the orchestrator and return the handlers from beforeUpdateHandlers(). List order is run order. The trigger must list before update and call TriggerOrchestrator.run(new ContactTriggerOrchestrator()).
apex
public with sharing class ContactTriggerOrchestrator implements TriggerOrchestrator.BeforeUpdate {
public List<BeforeUpdate.Handler> beforeUpdateHandlers() {
return new List<BeforeUpdate.Handler>{ new ContactPopulator(), new ContactValidator() };
}
}Good to Know
- The old row is read-only. Writing to
getOldSObject()or callingaddErroron it throws aFinalExceptionthat nocatchstops. The update fails. - No DML. If a handler runs DML or publishes an event, the library throws. Set fields with
record.put, and change other records from an AfterUpdate.Writer. - Updates re-enter. A later update of the same records in the transaction runs BeforeUpdate again. A Populator acts on a record at most 3 times per transaction by default. A Validator runs on every pass.
- Lookups hold only the Id.
((Contact) record.getNewSObject()).Accountis null. Declare a ParentQuery and readrecord.getNewParent('Account'). - One role per class. A class that implements both roles runs only as a Populator. List Populators first, so Validators see the values they set.
