Skip to content

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 calling addError on it throws a FinalException that no catch stops. 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()).Account is null. Declare a ParentQuery and read record.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.