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.

Comments (16) comments feed

Ideas coming in the Winter ’09 Release

I noticed that a recent idea of mine was marked as coming in the Winter ’09 release.  I decided to check if I could craft a URL to see others tagged as such and, sure enough, it worked.

http://ideas.salesforce.com/popular/coming_in_winter_09

Comments (6) comments feed

Auto vCard Usage thru July 08

Auto vCard usage continues to grow. The script received a high of 7425 hits in May 2008 only to drop off in June and July.  If history is an indication, it should pick up again this month.

The stats through July 2008 are below:

Auto vCard consists of 2 custom buttons (one each for Leads and Contacts) that allow you to create a vCard file for a specific record.  In other words, you click a button in Salesforce to save that contact/lead to Outlook or another PIM.

Comments off comments feed

Akismet PHP5 class updated

The Akismet PHP5 class I use for the Akismet Web-to-Lead Spam Check was updated.  The old version oftentimes hung as it established a session with Akismet.  No longer.  The new version is like butter.

I updated the Arrowpointe Google Code site and the latest version is in the download there.

Comments off comments feed

Proximity Searching in Salesforce.com (demo)

The recent release of Arrowpointe Maps introduced functionality to perform proximity searching within Salesforce.com.  We call it “Search Nearby”.  With the click of a button from a record in Salesforce, you can search for Leads, Accounts and/or Contacts that are within a user-specified proximity.

Click the image below is see a demo of the functionality in action.

About Arrowpointe Maps

Arrowpointe Maps is an on-demand mapping platform that facilitates a conversation between Salesforce.com & MapQuest allowing for easy deployment of mapping capabilities in your organization and providing end-users a simple means for mapping their data. Arrowpointe Maps is configurable and can be tailored to your organization, so that your users can work with their information in a meaningful way.

The official location for information on Arrowpointe Maps is its product page at http://www.arrowpointe.com/maps. There, you will find answers to the most frequently asked questions, screencasts and links to its AppExchange page.

Comments (2) comments feed

Next entries » · « Previous entries