function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
AVictorioAVictorio 

Bulkify Multi Object Trigger

I'm a total newbie to Apex and have diligently searched and searched through out the forums for a solution to my problem. I've managed to set up several triggers on my own but I'm facing difficulty with this one.  Anyways, here's my trigger code:

 

 

trigger UpdateHotelAndRFP on Opportunity (before insert, before update) {
    	
	for (Opportunity o : Trigger.new) {
	
	List<Lead> leads = [Select Id From Lead WHERE ConvertedOpportunityId = :o.Id];
	List<RFP__c> rfp = [Select Id From RFP__c WHERE Lead__c IN :leads];
        List<RFPResponse__c> rfps = [Select Id, Hotel__c, Contact__c, 
        Response_Exceptions__c, Comp_Room_Policy__c, Hotel_Cancel_Policy__c, 
        AdditionalFees__c From RFPResponse__c Where RFP_ID__c IN :rfp And Status__c = 'Awarded'];
   	
   		if (rfps.Size() > 0) {
   			
   			if(o.Hotel_Account_Name__c == null){
   			o.Hotel_Account_Name__c = rfps[0].Hotel__c;
   			}
   			if(o.Hotel_Contact__c == null) {
   			o.Hotel_Contact__c = rfps[0].Contact__c;
   			}
   			if(o.RFP_Response__c == null){
   			o.RFP_Response__c = rfps[0].Id;
   			o.Response_Exceptions__c = rfps[0].Response_Exceptions__c;
   			o.Cancellation_Attrition_Policy__c = rfps[0].Comp_Room_Policy__c;
   			o.Hotel_Cancel_Policy__c = rfps[0].Hotel_Cancel_Policy__c;
   			o.Porterage_Fees_Additional_Fees__c = rfps[0].AdditionalFees__c;
   			}

   		}
  	}
}

 

Essentially a Lead has one child object, who also has a child object.

 

 

  • Lead
    • RFP__c
      • RFPResponse__c

 

When a Lead is converted I want to bring across the RFPResponse child object and perform a few updates to the Opportunity based on the contents of the RFPResponse object.

 

This trigger works perfectly, except that i cannot use it on bulk updates. I get the SOQL exception 21+ queries error message.  Any help?

 

Best Answer chosen by Admin (Salesforce Developers) 
AVictorioAVictorio

So here's what it took to bulkify my trigger.. 

 

 

trigger UpdateHotelAndRFP on Opportunity (before insert, before update) {
	
	//Create Map references. Trigger maps to A, A maps to B, B maps to C.
  	Map<Id, Id> OpportunityToLeadMap= new Map<Id, Id>();
  	Map<Id, Id> LeadToRFPMap  = new Map<Id, Id>();
  	Map<Id, RFPResponse__c> RFPToRFPResponseMap = new Map<Id, RFPResponse__c>();
  	
  	//Map the Lead Ids by querrying the ConvertedOpportunityID to OpportunityId (Trigger.New)
  	for(Lead lead : [SELECT Id, ConvertedOpportunityId FROM Lead WHERE ConvertedOpportunityId in: trigger.new]){
    	OpportunityToLeadMap.put(lead.ConvertedOpportunityId, lead.Id);
  	}
  	
  	//Map the RFP Ids by querrying the Lead__c to the LeadIds from the map above
  	for(RFP__c rfp : [SELECT Id, Lead__c FROM RFP__c WHERE Lead__c in: OpportunityToLeadMap.values()]){
    	LeadToRFPMap.put(rfp.Lead__c, rfp.Id);
  	} 
  	
  	//Map the RFPResponse Ids by querrying the RFP_ID__c to the RFP Ids from the map above
  	for(RFPResponse__c rfps : [SELECT Id, Hotel__c, Contact__c, Response_Exceptions__c, Comp_Room_Policy__c,
    Hotel_Cancel_Policy__c, AdditionalFees__c, RFP_ID__c FROM RFPResponse__c WHERE 
    RFP_ID__c in: LeadToRFPMap.values() AND Status__c = 'Awarded']){
    	RFPToRFPResponseMap.put(rfps.RFP_ID__c, rfps);
  	}
    
  	for(Opportunity o : trigger.new){
  		
  		//Retreived mapped records specific to the current Opportunity record
  		
  		//Get leadid from OppportunityToLeadMap using current opportunity id
  		Id leadid = OpportunityToLeadMap.get(o.Id);
  		//Get rfpid from LeadToRFPMap using leadid reference above
  		Id rfpid = LeadToRFPMap.get(leadid);
  		
  		//Avoid "System.NullPointerException: Attempt to de-reference a null object" error by
  		//first verifying that your query produced results
  		if(RFPToRFPResponseMap.containsKey(rfpid)) {
  			
  			//If query produced results, then proceed to mapping the RFPResponse using the rfpid
  			//refrence above
  			RFPResponse__c RelatedRFPResponse = RFPToRFPResponseMap.get(rfpid);
  		
  			//Update Opportunity using values from RFPResponse (Opportunity-->Lead-->RFP-->RFPResponse-->Opportunity)
  			if(o.Hotel_Account_Name__c == null){
      			o.Hotel_Account_Name__c = RelatedRFPResponse.Hotel__c;  
    		}

    		if(o.Hotel_Contact__c == null) {
      			o.Hotel_Contact__c = RelatedRFPResponse.Contact__c;
    		}

    		if(o.RFP_Response__c == null){
    			o.RFP_Response__c = RelatedRFPResponse.Id;
      			o.Response_Exceptions__c = RelatedRFPResponse.Response_Exceptions__c;
      			o.Cancellation_Attrition_Policy__c = RelatedRFPResponse.Comp_Room_Policy__c;
      			o.Hotel_Cancel_Policy__c = RelatedRFPResponse.Hotel_Cancel_Policy__c;
      			o.Porterage_Fees_Additional_Fees__c = RelatedRFPResponse.AdditionalFees__c;
  			}
  		}
  	}
}

 

 

All Answers

dmchengdmcheng

Have you looked at the Bulkify section on this page:

http://wiki.developerforce.com/index.php/Apex_Code_Best_Practices

 

You will also need to use SOQL relationship queries so you can pull the Lead related records along with the Lead records and work on the related records through the Lead records.

http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_soql_relationships.htm

 

You may also need to use Maps to map Opp IDs to Lead records(+their related records)

 

Here's an outline of what you need to do:

* use trigger.map.keyset to extract the opp IDs into a set.

* do a SOQL relationship query to get the Leads and related records.  put the keyset in your where condition.

* possibly create a map of opp IDs to lead records.

* loop through your opps and perform your actions.  Use map.get method to get the lead that corresponds to the opp in the loop.

 

David

AVictorioAVictorio

So here's what it took to bulkify my trigger.. 

 

 

trigger UpdateHotelAndRFP on Opportunity (before insert, before update) {
	
	//Create Map references. Trigger maps to A, A maps to B, B maps to C.
  	Map<Id, Id> OpportunityToLeadMap= new Map<Id, Id>();
  	Map<Id, Id> LeadToRFPMap  = new Map<Id, Id>();
  	Map<Id, RFPResponse__c> RFPToRFPResponseMap = new Map<Id, RFPResponse__c>();
  	
  	//Map the Lead Ids by querrying the ConvertedOpportunityID to OpportunityId (Trigger.New)
  	for(Lead lead : [SELECT Id, ConvertedOpportunityId FROM Lead WHERE ConvertedOpportunityId in: trigger.new]){
    	OpportunityToLeadMap.put(lead.ConvertedOpportunityId, lead.Id);
  	}
  	
  	//Map the RFP Ids by querrying the Lead__c to the LeadIds from the map above
  	for(RFP__c rfp : [SELECT Id, Lead__c FROM RFP__c WHERE Lead__c in: OpportunityToLeadMap.values()]){
    	LeadToRFPMap.put(rfp.Lead__c, rfp.Id);
  	} 
  	
  	//Map the RFPResponse Ids by querrying the RFP_ID__c to the RFP Ids from the map above
  	for(RFPResponse__c rfps : [SELECT Id, Hotel__c, Contact__c, Response_Exceptions__c, Comp_Room_Policy__c,
    Hotel_Cancel_Policy__c, AdditionalFees__c, RFP_ID__c FROM RFPResponse__c WHERE 
    RFP_ID__c in: LeadToRFPMap.values() AND Status__c = 'Awarded']){
    	RFPToRFPResponseMap.put(rfps.RFP_ID__c, rfps);
  	}
    
  	for(Opportunity o : trigger.new){
  		
  		//Retreived mapped records specific to the current Opportunity record
  		
  		//Get leadid from OppportunityToLeadMap using current opportunity id
  		Id leadid = OpportunityToLeadMap.get(o.Id);
  		//Get rfpid from LeadToRFPMap using leadid reference above
  		Id rfpid = LeadToRFPMap.get(leadid);
  		
  		//Avoid "System.NullPointerException: Attempt to de-reference a null object" error by
  		//first verifying that your query produced results
  		if(RFPToRFPResponseMap.containsKey(rfpid)) {
  			
  			//If query produced results, then proceed to mapping the RFPResponse using the rfpid
  			//refrence above
  			RFPResponse__c RelatedRFPResponse = RFPToRFPResponseMap.get(rfpid);
  		
  			//Update Opportunity using values from RFPResponse (Opportunity-->Lead-->RFP-->RFPResponse-->Opportunity)
  			if(o.Hotel_Account_Name__c == null){
      			o.Hotel_Account_Name__c = RelatedRFPResponse.Hotel__c;  
    		}

    		if(o.Hotel_Contact__c == null) {
      			o.Hotel_Contact__c = RelatedRFPResponse.Contact__c;
    		}

    		if(o.RFP_Response__c == null){
    			o.RFP_Response__c = RelatedRFPResponse.Id;
      			o.Response_Exceptions__c = RelatedRFPResponse.Response_Exceptions__c;
      			o.Cancellation_Attrition_Policy__c = RelatedRFPResponse.Comp_Room_Policy__c;
      			o.Hotel_Cancel_Policy__c = RelatedRFPResponse.Hotel_Cancel_Policy__c;
      			o.Porterage_Fees_Additional_Fees__c = RelatedRFPResponse.AdditionalFees__c;
  			}
  		}
  	}
}

 

 

This was selected as the best answer