BeforeDelete.Writer
Change other records or publish platform events before the delete, through a unit of work.
Signature
apex
public interface Writer extends BeforeDelete.Handler {
Boolean writeOnBeforeDeleteWhen(TriggerTypes.DeleteRecord record);
void writeOnBeforeDelete(TriggerTypes.DeleteRecord record, TriggerTypes.UnitOfWork unitOfWork);
}writeOnBeforeDeleteWhen(record): called once per record in the chunk. Returntrueto write for the record.writeOnBeforeDelete(record, unitOfWork): called right after its predicate returns true, with this Writer’s unit of work.
Example
apex
public with sharing class ContactWriter implements BeforeDelete.Writer {
public Boolean writeOnBeforeDeleteWhen(TriggerTypes.DeleteRecord record) {
return record.isNotNull(Contact.AccountId);
}
public void writeOnBeforeDelete(TriggerTypes.DeleteRecord record, TriggerTypes.UnitOfWork unitOfWork) {
unitOfWork.toInsert(new Task(WhatId = ((Contact) record.getOldSObject()).AccountId, Subject = 'Review contact'));
}
}apex
public with sharing class ContactReportsReassignWriter implements BeforeDelete.Writer, BeforeDelete.RelatedQuery {
public Map<String, BeforeDelete.RecordsProvider> queryRelatedOnBeforeDelete() {
return new Map<String, BeforeDelete.RecordsProvider>{ 'directReports' => new DirectReportsProvider() };
}
public Boolean writeOnBeforeDeleteWhen(TriggerTypes.DeleteRecord record) {
return !record.getRelated('directReports').getAllWhereKeyEquals(record.getId()).isEmpty();
}
public void writeOnBeforeDelete(TriggerTypes.DeleteRecord record, TriggerTypes.UnitOfWork unitOfWork) {
Id managerId = ((Contact) record.getOldSObject()).ReportsToId;
for (SObject report : record.getRelated('directReports').getAllWhereKeyEquals(record.getId())) {
unitOfWork.toUpdate(new Contact(Id = report.Id, ReportsToId = managerId));
}
}
private without sharing class DirectReportsProvider implements BeforeDelete.RecordsProvider {
public List<SObject> query(TriggerTypes.DeleteRecords records) {
return [SELECT Id, ReportsToId FROM Contact WHERE ReportsToId IN :records.getIds() AND Id NOT IN :records.getIds()];
}
public String keyOf(SObject row) {
return ((Contact) row).ReportsToId;
}
}
}Rules
- Register, do not run DML.
toInsert,toUpdate,toUpsert,toDeleteandtoPublish(platform events) take one record each. - Commits before the delete. The shared unit of work commits once, after the last handler of the run, while the records still exist. A Writer with ContinueOnError gets an automatic unit, committed right after it. OwnUnitOfWork supplies your own.
- Links still point at the rows. Records that look up to the deleted rows still hold the Ids here, so a query by
records.getIds()finds them. - Validator wins. A class that also implements
BeforeDelete.Validatorruns only as a Validator.
WARNING
Leave the records being deleted alone. A toDelete of one fails the commit with SELF_REFERENCE_FROM_TRIGGER, and an update of one is lost when the delete goes through.
Test
apex
@IsTest
static void writeOnBeforeDeleteWhenQualifiesContactWithAccount() {
// Setup
Contact oldContact = new Contact(AccountId = new TriggerTypes.RandomIdGenerator().get(Account.SObjectType));
// Test
Boolean qualifies = new ContactWriter().writeOnBeforeDeleteWhen(new TriggerTypes.TriggerRecord(null, oldContact));
// Verify
Assert.isTrue(qualifies, 'A contact with an account should qualify.');
}