Bulkifying a Trigger (an example)
Back in June, I blogged about a trigger I made to auto-create a Campaign Member record for new Leads if the Lead Source value matched the Name of an existing Campaign. Steve Andersen rightfully commented on that post about the need to bulkify the trigger. I have been pretty slow in learning Apex and over the past week or so things have started clicking for me. I am now (finally) beginning to “get it”. With the help of Matt Kaufman and a friend at Salesforce, I have come to understand Apex a bit more and can much more easily apply it to my day to day Salesforce work.
Therefore, I thought I should correct the trigger I wrote and make it bulkified. This should serve as a good before and after example of a trigger that accomplishes the same thing, but is now bulkified.
The original Trigger
Below is how the trigger looked originally.
trigger Create_CampaignMember_For_New_Leads on Lead (after insert) {
try {
if (Trigger.new.size() == 1) {
List <CampaignMember> cm = new list<CampaignMember>();
for(Lead L : Trigger.new) {
String cname = L.leadsource;
// Added for AppExchange Partners that get leads via AppExchange where Salesforce added the "dup-" term to signify a duplicate
String replaceText2 = 'dup-';
cname = cname.replace(replaceText2,'');
List <Campaign> c = [select id, name from Campaign where name = :cname limit 1];
if(!c.isEmpty()){
CampaignMember cml = new CampaignMember();
cml.campaignid = c[0].id;
cml.leadid = l.id;
cm.add(cml);
}
}
if(!cm.isEmpty()){
insert cm;
}
}
} catch(Exception e) {
system.debug ('error: ' + e.getMessage() );
}
}
Bulkified Trigger
And now a corrected version to be bulkified.
trigger Create_CampaignMember_For_New_Leads on Lead (after insert) {
/*
* Loop through all leads and collect the necessary lists
*/
list<Lead> theLeads = new list<Lead>(); // List containing each Lead being processed
list<String> cNames = new list<String>(); // List of Campaign Names
map<String, String> map_lSource_to_cName = new map<String, String>(); // Mapping Lead Sources to Campaign Names. We have this because we are cleaning up Lead Sources in some cases.
String wrkText = ''; // Temporary, working variable
String replaceText = 'dup-'; // Text to replace. This is included for ISV partners who want to remove the "-dup" string that is included for duplicate AppExchange Lead Submissions
for(Lead l:trigger.new) {
theLeads.add(l); // add lead to the main lead list
if (l.leadsource != null) {
wrkText = l.leadsource;
wrkText = wrkText.replace(replaceText,'');
cNames.add(wrkText); // add to list of Campaign Names
map_lSource_to_cName.put(l.leadsource,wrkText); // add to map of Lead Sources to Campaign Names
}
}
/*
* Create a map containing an association of Campaign Names to Campaign IDs
*/
list<Campaign> theCampaigns = [SELECT Id, Name FROM Campaign WHERE Name IN :cNames]; // Campaign sObjects we are dealing with
map<String, ID> map_cName_to_cID = new map<String, ID>(); // Mapping Campaign Names to Campaign IDs
for (Campaign c:theCampaigns) {
map_cName_to_cID.put(c.Name,c.Id);
}
/*
* Loop through the main list of Leads
*/
list <CampaignMember> theCampaignMembers = new list<CampaignMember>(); // List containing Campaign Member records to be inserted
for (Lead l:theLeads) {
if(map_cName_to_cID.get(map_lSource_to_cName.get(l.leadsource)) != null) {
CampaignMember cml = new CampaignMember();
cml.leadid = l.id;
cml.campaignid = map_cName_to_cID.get(map_lSource_to_cName.get(l.leadsource));
theCampaignMembers.add(cml);
}
}
/*
* Insert the list of Campaign Members
*/
if(!theCampaignMembers.isEmpty()){
insert theCampaignMembers;
}
}
Updated Test Class (100% code coverage)
I updated the test class too to make it cover 100% of the code.
public class Create_CampaignMember_For_New_Leads {
static testMethod void Create_CampaignMember_For_New_Leads() {
// Create a Campaign to be matched on
Campaign C1 = new Campaign();
C1.Name = 'Matching Campaign';
insert C1;
// Create a Lead with a Lead Source matching a Campaign
Lead L1 = new Lead();
L1.lastname = 'Create_CampaignMember_For_New_Leads';
L1.firstname = 'Test For';
L1.company = 'Company ABC';
L1.leadsource = 'Matching Campaign';
insert L1;
String holder = L1.id;
List <CampaignMember> cm = [select id from CampaignMember where leadid = :holder limit 1];
system.AssertEquals(1,cm.size());
// Create a Lead without a Lead Source matching a Campaign
Lead L2 = new Lead();
L2.lastname = 'Create_CampaignMember_For_New_Leads';
L2.firstname = 'Test For';
L2.company = 'Company ABC';
L2.leadsource = 'No Matching Campaign';
insert L2;
String holder2 = L2.id;
List <CampaignMember> cm2 = [select id from CampaignMember where leadid = :holder2 limit 1];
system.AssertEquals(0,cm2.size());
}
}
Feel free to make additional recommendations on this code. I updated the Arrowpointe Google Code Site with this code too.


