fflib-apex-common
fflib-apex-common copied to clipboard
Improvements to Selector API
This was spawned from another issue 123...
"I couldn't agree more of the need to centralize queries and leverage common field lists. Avoiding the missing field error at runtime is something we all can agree on.
As you know, the book constructs SOQL queries by fleshing out via String.format(..) upon a query string 'template' populated from calls to various selector methods to get fields or order bys. This was clear and, to someone looking at the code, clearly shows what is being queried (parent relationships, child relationships, where clause, etc.)
But now, the code base has evolved into using QueryFactory methods to construct the query. This construction process involves calling on the selector methods to provide fields. The fluent pattern is a lovely pattern to use but only if each method is clear to its intent and is easy to use.
Here is where it became mind-bending
Taking the classic SFDC query on Opportunity and Products such as
select id, close date, account.name, account.website, (select id, unitprice, pricebookEntry.Pricebook.Name, pricebookEntry.product2.productCode from OpportunityLineItems order by ...) from Opportunity where ... You have such an example in fflib-apex-common-samplecode within OpportunitiesSelector.cls but it is a mish-mash of fflib_SobjectSelector methods to associate subqueries to the main query (the configureXXX methods) and some fflib_QueryFactory methods.
I would have liked to see the "classic example" above done entirely using fluent fflib_QueryFactory methods with no fflib_SObjectSelector methods being involved in attaching subqueries to the main query.
To make things more readable by inspection, the fluent fflib_QueryFactory methods should, when read on the page, look like they are building the SOQL query starting from the main select, lookup selects, subqueries, where clause, orderby, and limit. Arguments to these fluent methods can come from selector methods. But the selector methods, if they build structure, get confusing.
I want to pound out selector methods just like I would writing native Apex SOQL, but the methods I use in the fluent pattern have to match in sequence the way a classic APEX SOQL query is written and I shouldn't have to think about the 'hidden' behavior of selector methods that affect the relationship queries within my SOQL.
In the example below, and I'm making this up here and probably there is a better way but I was kind of looking for something fluent but structured in the same sequence as the query you would write 'normally'.
fflib_QueryFactory oppoQf =
newQueryFactory() // use default getSobjectFields for OpportunitySelector
.selectFields('Account',new AccountSelector().getSObjectFields()) // parent Account fields
.subSelectQueries(new List<QueryFactory> { // list of subqueries, each configured fluently
new OpportunityLineItemSelector().newQueryFactory(false)
.selectFields(new List<String>{'UnitPrice','ListPrice'})
.from('OpportunityLineItems') // somehow, the relationship name has to be specified
.selectFields('PricebookEntry.Product2',new Product2Selector().getSobjectFields()
.selectFields('PricebookEntry.Pricebook2',new Pricebook2Selector().getSobjectFields()
}
.setCondition('ID in :idSet')
.addOrdering(...);
return Database.query(oppoQf.toSoql());
Even if the above isn't a good idea for some reason, at least an update of the examples to use solely query factory methods for the Opportunities with parent account and children OLI (and lookups from the children) would be immensely helpful."