There's a couple issues here:
- You have multiple SOQL queries within a for loop
- You're doing a callout within a for loop
The reason both are issues are due to the limits per transaction and this is all occurring in a single transaction. You'll run into the following limits:
- Too many SOQL queries - This'll occur if
conlist
is > 49
- Too many callouts - This'll occur, even if you fix your SOQL queries limit, when
conlist
is > 100
In terms of fixing the too many SOQL queries, you need to analyze why you need to do it in a loop. In your case, it doesn't seem to be necessary. What you want is the list of related objects per Contact. You can get that with one query by leveraging Maps before you hit a for loop.
Below is an example of how you can do all the work at once (so only 3 queries) regardless of the size of conlist
. That is what we mean when we say "bulkified" - regardless of size, the code will not run into governor limits.
List<Contact> conlist = [SELECT Id from Contact where AccountId IN :accid];
//do your related SOQL queries for all records in the batch
List<Related_Object1__c> relObj1 = [SELECT Id FROM Related_Object1__c WHERE ContactId__c IN :conlist];
List<Related_Object2__c> relObj2 = [SELECT Id FROM Related_Object2__c WHERE ContactId__c IN :conlist];
//build map of related objects by Contact Id
Map<Id,List<Related_Object1__c>> relatedObject1ByContactId = new Map<Id,List<Related_Object1__c>>();
for(Related_Object1__c obj1 : relObj1){
if(relatedObject1ByContactId.contains(obj1.ContactId__c)){
relatedObject1ByContactId.get(obj1.ContactId__c).add(obj1);
} else {
relatedObject1ByContactId.put(obj1.ContactId__c, new List<Related_Object1__c> { obj1 });
}
}
//do the same for Related_Object2__c
//pass everything to a method that can do the callout
calloutclass.calloutmethod(relatedObject1ByContactId, relatedObject2ByContactId);
So the above solves issue #1, but too many callouts will still occur. If you're doing more than 100 callouts in a single transaction, you should be looking into a batchable class or a queueable class that won't have to process it all in one transaction. You can pass the information needed to the queueable (related objects, how many have been processed, what's left, etc) so that it keeps enqueuing itself until all callouts have been made.
public class handleCallouts implements Queueable, Database.AllowsCallouts {
Map<Id, List<Related_Object1__c>> relObj1ByContactIds;
Map<Id, List<Related_Object2__c>> relObj2ByContactIds;
Integer recordsProcessed;
//pass in map of related info for queueable to process
public handleCallouts(Map<Id,List<Related_Object1__c>> relObj1ByContactIds, Map<Id, List<Related_Object1__c>> relObj1ByContactIds, Integer recordsProcessed){
this.relObj1ByContactIds = relObj1ByContactIds;
this.relObj2ByContactIds = relObj2ByContactIds
this.recordsProcessed = recordsProcessed;
}
public void execute(QueueableContext qc) {
//do callout, build body and keep incremental count
//if you hit 100, enqueue another job to run to finish the rest
//Pass in maps and where you left off
}
}