2

I have a trigger that when a campaign member record is created or updated it will write the campaign ID to a field either on the Lead or Contact.
I started with this code but it was not Bulkifed as my SOQL was inside a FOR.

trigger CurrentCampaign on CampaignMember (before insert, before update) {

List<Lead> newLeads = new List<Lead>();
List<Contact> newContacts = new List<Contact>();


for(CampaignMember cm : Trigger.new){
    if(cm.LeadId != Null && cm.ContactId == Null){
       List<Lead> lu = [SELECT Id, Current_Campaign__c FROM Lead 
                           WHERE Id = :cm.LeadId];
       if (lu.size() > 0) {
           for(Integer i = 0; i < lu.size(); i++){
               lu[i].Current_Campaign__c = cm.CampaignId;
               newLeads.add(lu[i]);
               }
           }
        }
    else if(cm.ContactId != Null){
        List<Contact> cu = [SELECT Id, Current_Campaign__c From Contact
                                WHERE Id =:cm.ContactId];
        if (cu.size() > 0) {
            for(Integer i = 0; i < cu.size(); i++){
              cu[i].Current_Campaign__c = cm.CampaignId;
              newContacts.add(cu[i]);
              }
           }
        }   
}

Upsert newLeads;
Upsert newContacts;
}



So after fumbling around I got it down to this.

trigger CurrentCampaign on CampaignMember (before insert, before update) {

List<Lead> newLeads = new List<Lead>();
List<Contact> newContacts = new List<Contact>();


for(CampaignMember cm : Trigger.new){
    if(cm.LeadId != Null && cm.ContactId == Null){
               cm.Lead.Current_Campaign__c = cm.CampaignId;
               newLeads.add(cm.Lead);
               }

    else if(cm.ContactId != Null){
              cm.Contact.Current_Campaign__c = cm.CampaignId;
              newContacts.add(cm.Contact);
              }
           } 

 Upsert newLeads;
 Upsert newContacts;
   }



Then I got this test.

@isTest

public class currentCampaignTest{
static testMethod void CurrentCampaign(){

    Account testAccount = new Account(Name = 'Testy McTesterson');
    Contact testContact = new Contact(FirstName = 'TestyMc' , LastName = 'Testerson', Title = 'TestyMcTesting' , Email = 'TestingMcTesterson@testing.com');
    Lead testLead = new Lead(LastName = 'TestyMcTesterson' , Company = 'Nowaythistestcanmatchanything');
    Campaign testCampaign = new Campaign(Name = 'TestingMcTesterson Campaign', IsActive = TRUE);

    insert testAccount;
    insert testContact;
    insert testLead;
    insert testCampaign;

    CampaignMember cm0 = new CampaignMember(CampaignId = testCampaign.Id, ContactId = testContact.Id, LeadId = NULL, Status ='Sent');

    CampaignMember cm1 = new CampaignMember(CampaignId = testCampaign.Id, ContactID = NULL , LeadId = testLead.Id, Status ='Sent');

    insert cm0;
    insert cm1;

    List<CampaignMember> cm2 = [SELECT id, Status FROM CampaignMember LIMIT 201];
    for(Integer i=0; i<cm2.size(); i++){
        cm2[i].Status = 'Responded';
        }

    update cm2;
    }
  }



Now I keep getting this error when I run the test.

Error Message   System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, CurrentCampaign: execution of BeforeInsert

caused by: System.NullPointerException: Attempt to de-reference a null object

Trigger.CurrentCampaign: line 14, column 1: []
Stack Trace Class.currentCampaignTest.CurrentCampaign: line 20, column 1



I understand this is when you try and reference something that hasn't been set yet but on Line20 of the test I am trying to insert a new CampaignMember with a Null LeadID. This seems to fail at "cm.Contact.Current_Campaign__c = cm.CampaignId" when I try and reference the Contact related to the CampaignMember Record.

I am still pretty new to all this Apex and coding base and I can't figure out why it wont insert the record.

  • 2
    (1) this is a common misconception - triggers don't have context to relationship fields as in cm.Contact.current_campaign__c - you need to query for the parents in the trigger. Trigger.new only knows about the fields in CampaignMember. (2) For clarity - delete the non bulkified code. – cropredy Nov 30 '15 at 23:42
  • 2
    nope - you create two maps - contactIdToContactMap, and leadIdToleadMap that you build from one SOQL query each. Then you have all you need. The Contacts and Leads you pull in are only the ones referenced in the cm.leadId or cm.contactId fields. Very efficient. – cropredy Nov 30 '15 at 23:49
  • 2
    Maps are your friend. You can't really do bulkified triggers without them. Plenty of examples to look at – cropredy Dec 1 '15 at 0:07
  • 1
    don't forget to think about 2+ campaignMembers for the same Lead (or Contact) but with different campaignIds - you have to decide which one is the 'primary campaign' – cropredy Dec 1 '15 at 0:46
  • 1
    yes - but a mass update of many campaignMembers through Data Loader could bring in multiple CMs pointing at different Campaigns but sharing the same Lead – cropredy Dec 1 '15 at 0:54
1

Thanks to @crop1645 I updated the code to use Maps.

trigger CurrentCampaign on CampaignMember (before insert, before update) {

//make my lists for loading
List<Lead> LoadLeadData = new List<Lead>();
List<Contact> LoadContactData = new List<Contact>();

//Set a varible to hold my Lead ID's
Set<id> CampLeadId = new Set<id>();

//Populate the variable
for(CampaignMember cm : Trigger.new){
    if(cm.LeadId != Null && cm.ContactId == Null){
        CampLeadId.add(cm.LeadId);
        }
    }

//Make the Lead Map
Map<id, Lead> LeadMap = new Map<id, Lead>([SELECT Id, Current_Campaign__c FROM Lead WHERE Id IN :CampLeadId]);

//Set a variable to hold my Contact ID's
Set<id> CampContactId = new Set<id>();

//Populate the variable with Contacts that match
for(CampaignMember cm : Trigger.new){
    if(cm.ContactId != Null){
        CampContactId.add(cm.ContactId);
        }
     }

//Make the Contact Map
Map<id, Contact> ContactMap = new Map<id, Contact>([SELECT Id, Current_Campaign__c FROM Contact WHERE Id IN :CampContactId]);


//go through the map and make the edits I need
for(CampaignMember cm : Trigger.new){
    if(cm.LeadId != Null && cm.ContactId == Null){
        LeadMap.get(cm.LeadId).Current_Campaign__c = cm.CampaignId;

       //Add these changes to the list to load after the loop
        LoadLeadData.add(LeadMap.get(cm.LeadId));
               }
//now do it for contacts
    else if(cm.ContactId != Null){
        ContactMap.get(cm.ContactId).Current_Campaign__c = cm.CampaignId;

        //Add these changes to the list to load after the loop
        LoadContactData.add(ContactMap.get(cm.ContactId));
        }
    }

//Load the Data Changes
Upsert LoadContactData;
Upsert LoadLeadData;
}

The biggest duh hurdle for me was that with the Map you can dump an entire item into it like Lead, so it then can use any field that is contained inside Lead.

This website helped me a bunch
http://blog.jeffdouglas.com/2011/01/06/fun-with-salesforce-collections/

| improve this answer | |
  • 2
    great work Dan -- I had deliberately not given you the code sample for you as I deduced you wanted to learn the process of bulkifying yourself. You could simplify the above a bit by building CampContactId and CampLeadId in the same loop through Trigger.new. You are making an implicit assumption that in a batch of CampaignMembers where record i and j are the same LeadId but different Campaign parent, that the LeadId will get the primary campaign of record j. An alternate strategy would be to pick the most recent campaign or the campaign with the biggest budget – cropredy Dec 1 '15 at 19:47
  • With our current process the assumption of having an issue with i and j and having j win is ok for now. Thanks for the tip on building the two Sets in the same loop! – Dan Dec 1 '15 at 20:59
  • cool - you might want to comment your production code re: the i vs j issue so your future replacement as you move onto bigger and better things gets a helpful hint. – cropredy Dec 1 '15 at 21:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.