BeforeInsert.Populator
Sets fields on new records before they are saved. The values save with the record: no DML and no second save.
Interface
apex
public class BeforeInsert {
public interface Populator extends Handler {
Boolean populateOnBeforeInsertWhen(TriggerHandler.InsertRecord record);
void populateOnBeforeInsert(TriggerHandler.InsertRecord record);
}
}populateOnBeforeInsertWhen(record): called once per record in the chunk. Returntrueto populate the record.populateOnBeforeInsert(record): called right after its predicate returns true, for that record.
Example
apex
public with sharing class ContactPopulator implements BeforeInsert.Populator {
public Boolean populateOnBeforeInsertWhen(TriggerHandler.InsertRecord record) {
return record.isBlank(Contact.LeadSource);
}
public void populateOnBeforeInsert(TriggerHandler.InsertRecord record) {
record.put(Contact.LeadSource, 'Web');
}
}cls
public with sharing class ContactEmailNormalizationPopulator implements BeforeInsert.Populator {
public Boolean populateOnBeforeInsertWhen(TriggerHandler.InsertRecord record) {
return record.isNotBlank(Contact.Email);
}
public void populateOnBeforeInsert(TriggerHandler.InsertRecord record) {
Contact contactRecord = (Contact) record.getNewSObject();
record.put(Contact.Email, contactRecord.Email.trim().toLowerCase());
}
}cls
public with sharing class OpportunityCloseDatePopulator implements BeforeInsert.Populator {
public Boolean populateOnBeforeInsertWhen(TriggerHandler.InsertRecord record) {
return record.isNull(Opportunity.CloseDate);
}
public void populateOnBeforeInsert(TriggerHandler.InsertRecord record) {
record.put(Opportunity.CloseDate, this.endOfCurrentQuarter());
}
private Date endOfCurrentQuarter() {
Date today = Date.today();
Integer lastMonthOfQuarter = ((today.month() - 1) / 3 + 1) * 3;
return Date.newInstance(today.year(), lastMonthOfQuarter, 1).addMonths(1).addDays(-1);
}
}Good to Know
putwrites to the row being saved. Handlers listed later see the value.- No SOQL in the loop. The predicate and the action run once per record. Read parents with a ParentQuery and other records with a RelatedQuery.
- No DML. If the handler runs DML or publishes an event, the library throws. Change other records from an AfterInsert.Writer.
- Populator wins. A class that also implements
BeforeInsert.Validatorruns only as a Populator. - Exceptions fail the save. Without ContinueOnError, an exception fails every record in the chunk. With it, the exception is logged and the save goes on.
Test
apex
@IsTest
static void populateOnBeforeInsertLowercasesEmail() {
// Setup
Contact newContact = new Contact(Email = ' Jane.Doe@Example.com ');
// Test
new ContactEmailNormalizationPopulator().populateOnBeforeInsert(new TriggerHandler.TriggerRecord(newContact, null));
// Verify
Assert.areEqual('jane.doe@example.com', newContact.Email, 'The email should be trimmed and lowercased.');
}