I've created a generic selector class as follows:
public inherited sharing class DAO_SObjectSelector {
public List<SObject> selectFor(Query query) {
return Database.query(query.toString());
}
public class Query {
SObjectType sObjectType;
List<SObjectField> queryFieldList;
List<String> whereFilterList = new List<String>();
List<SortField> sortFieldList = new List<SortField>();
public Query(SObjectType sObjectType, List<SObjectField> sObjectFieldList) {
this.sObjectType = sObjectType;
this.queryFieldList = sObjectFieldList;
}
public Query addWhereFilter(String whereFilter) {
this.whereFilterList.add(whereFilter);
return this;
}
public Query addSortField(SObjectField sortField, String sortDirection) {
this.sortFieldList.add(new SortField(sortField, sortDirection));
return this;
}
public override String toString() {
String queryString = 'SELECT ' + String.join(this.getQueryFieldNameList(), ',');
queryString += ' FROM ' + this.sObjectType.getDescribe().getName();
if (!this.whereFilterList.isEmpty()) {
queryString += ' WHERE ' + String.join(this.whereFilterList, ' AND ');
}
if (!this.sortFieldList.isEmpty()) {
queryString += ' ORDER BY ' + String.join(this.getSortOrderList(), ',');
}
System.debug('######### queryString: ' + queryString);
return queryString;
}
private List<String> getQueryFieldNameList() {
List<String> sObjectFieldNameList = new List<String>();
for (SObjectField sObjectField : this.queryFieldList) {
sObjectFieldNameList.add(sObjectField.getDescribe().getName());
}
System.debug('######### sObjectFieldNameList: ' + sObjectFieldNameList);
return sObjectFieldNameList;
}
private List<String> getSortOrderList() {
List<String> sortOrderList = new List<String>();
for (SortField sortField : this.sortFieldList) {
sortOrderList.add(sortField.toString());
}
return sortOrderList;
}
}
private class SortField {
SObjectField sortField;
String sortDirection;
public SortField(SObjectField sortField, String sortDirection) {
this.sortField = sortField;
this.sortDirection = sortDirection;
}
public override String toString() {
return this.sortField.getDescribe().getName() + ' '
+ ((!String.isBlank(this.sortDirection)) ? this.sortDirection : '');
}
}
}
I am trying to consume it with the following:
private List<Tower__c> getTowerList() {
List<SObjectField> sObjectFieldList = new List<SObjectField>{
Tower__c.Id, Tower__c.Name,
Tower__c.State__r.Name,
Tower__c.Tower_Location__Latitude__s, Tower__c.Tower_Location__Longitude__s
};
return this.sObjectSelector.selectFor(
new DAO_SObjectSelector.Query(Tower__c.SObjectType, sObjectFieldList)
.addSortField(Tower__c.Name, 'ASC')
);
}
However, this fails with:
duplicate field selected: Name
because both Tower__c.Name
and Tower__c.State__r.Name
are being joined to the SOQL query as simply "Name". In other words the query is being rendered as:
SELECT Id,Name,Name,Tower_Location__Latitude__s,Tower_Location__Longitude__s FROM Tower__c ORDER BY Name ASC
How can I modify the DAO_SObjectSelector.Query class to make this work as expected?