Skip to content

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. Return true to 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

  • put writes 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.Validator runs 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.');
}