Are you an LLM? You can read better optimized documentation at /after-delete/add-ons/related-query.md for this page in Markdown format
AfterDelete.RelatedQuery
Loads records other than parents with one query per provider, instead of SOQL per record. Use it to recompute a former parent from the records that remain.
Interface
apex
public class AfterDelete {
public interface RelatedQuery {
Map<String, AfterDelete.RecordsProvider> queryRelatedOnAfterDelete();
}
}queryRelatedOnAfterDelete(): called once per chunk, at this handler’s turn, before its first predicate. Returns provider name →RecordsProvider.
RecordsProvider
apex
public class AfterDelete {
public interface RecordsProvider {
List<SObject> query(TriggerHandler.DeleteRecords records);
String keyOf(SObject record);
}
}query(records): called once per provider, at the handler’s turn, with every record in the chunk. Returns the rows to index by key;nullcounts as no rows.keyOf(record): called once per row thatqueryreturned. Returns the key thatgetFirstWhereKeyEqualsandgetAllWhereKeyEqualsmatch;nullleaves the row out of the index.
Example
apex
public with sharing class ContactWriter implements AfterDelete.Writer, AfterDelete.RelatedQuery {
public Map<String, AfterDelete.RecordsProvider> queryRelatedOnAfterDelete() {
return new Map<String, AfterDelete.RecordsProvider>{ 'accountContacts' => new AccountContactsProvider() };
}
public Boolean writeOnAfterDeleteWhen(TriggerHandler.DeleteRecord record) {
return record.isNotNull(Contact.AccountId);
}
public void writeOnAfterDelete(TriggerHandler.DeleteRecord record, TriggerHandler.UnitOfWork unitOfWork) {
Id accountId = ((Contact) record.getOldSObject()).AccountId;
Integer contactCount = record.getRelated('accountContacts').getAllWhereKeyEquals(accountId).size();
unitOfWork.toInsert(new Task(WhatId = accountId, Subject = 'Contacts on the account: ' + contactCount));
}
private with sharing class AccountContactsProvider implements AfterDelete.RecordsProvider {
public List<SObject> query(TriggerHandler.DeleteRecords records) {
return [SELECT Id, AccountId FROM Contact WHERE AccountId IN :records.getIdsOf(Contact.AccountId)];
}
public String keyOf(SObject record) {
return ((Contact) record).AccountId;
}
}
}Good to Know
- Deleted rows are invisible. A query by
records.getIds()finds nothing, with no error. Key providers by the old lookups:records.getIdsOf(Contact.AccountId). - Inbound lookups may be stale. Records that looked up to a deleted row may still point at it during this trigger. Exclude them with
AND ReportsToId NOT IN :records.getIds(). - Providers run even when nothing qualifies. They run before the first predicate and cost their SOQL on every chunk.
- Write once per parent. Two deleted contacts of one account both see the same remaining contacts. Write to the account once from a Finalizer.
- Set sharing on the provider. Its SOQL runs under the provider class's own sharing keyword. An inner class does not inherit its outer class's keyword.
