Intro
Fetching Records using a collection of Ids in Flow Builder should be a simple job, but somehow SF is still missing this ability. This limits the enforcing of best practices since sometimes we might have no choice but use GET elements inside a flow-loop.
The idea with this article is to show a simple implementation of an Apex Action to handle this limitation.
Basic Implementation
I'm assuming that the reader is already familiar with Invocable Methods/Variables and how to call Apex from a Flow.
That being said, we have this apex class that can solve our issue. Let's assume we want to fetch Contact records.
public with sharing class FlowQueryHelper{
@InvocableMethod(label = 'GET Contacts where Ids in Collection')
public static List<Output> getContactsInCollection(List<Input> params){
String ids = params[0].ids;
List<Contact> contacts = [
SELECT Id, Name
FROM Contact
WHERE Id IN :ids];
Output output = new Output();
output.contacts = contacts;
return new List<Output>{ output };
}
public class Input{
@InvocableVariable(label = 'Text collection variable (Ids only)' required = true)
public List<String> ids;
}
public class Output{
@InvocableVariable(label = 'Contacts Collection Variable')
public List<Contact> contacts;
}
}
That works like a charm, but we can make it a little bit more generic in case we want to reutilize this class with other Object types and you probably will.
More Generic
Our class must be able to run for any custom or standard sObject. This means that it will be unaware of the sObject API Name and Fields in order to run for any collection of Ids we pass to it.
API Name
We can get the API Name directly from the Id by using describe methods. More details here.
private static String getObjectName(String objId){
return ((Id)objId).getSObjectType().getDescribe().getName();
}
Fields
Again we're making use of the well known Schema class and describe methods. More details here.
private static String getObjectFields(String objectName) {
List<String> fields = new List<String>(Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap().keySet());
return String.join(fields, ',');
}
SOQL Query
And now we have the SOQL statement. We'll be using Dynamic SOQL to fetch our records. More details here.
Database.query('SELECT ' + String.escapeSingleQuotes(objectFields) + ' FROM ' + String.escapeSingleQuotes(objectName) + ' WHERE Id IN :ids')
Final Version
public with sharing class FlowQueryHelper{
@InvocableMethod(label = 'GET Records where Ids in Collection')
public static List<Output> getRecordsInCollection(List<Input> params){
String objectName = getObjectName(params[0].ids[0]);
String objectFields = getObjectFields(objectName);
List<String> ids = params[0].ids;
List<sObject> records = Database.query('SELECT ' + String.escapeSingleQuotes(objectFields) + ' FROM ' + String.escapeSingleQuotes(objectName) + ' WHERE Id IN :ids');
Output output = new Output();
output.records = records;
return new List<Output>{ output };
}
private static String getObjectName(String objId){
return ((Id)objId).getSObjectType().getDescribe().getName();
}
private static String getObjectFields(String objectName) {
List<String> fields = new List<String>(Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap().keySet());
return String.join(fields, ',');
}
public class Input{
@InvocableVariable(label = 'Text Collection variable (Ids only)' required = true)
public List<String> ids;
}
public class Output{
@InvocableVariable(label = 'Record Collection Variable')
public List<sObject> records;
}
}
And that's it, folks. Screenshot below as example for Account object.
Conclusion
This will do the trick at least until SF comes up with a more final/solid solution. It's important to notice that when calling this action you must choose the Object type for action output and assign it to a variable of the same type.
Top comments (1)
Awesome!