Last active
August 29, 2015 14:02
-
-
Save afawcett/659211d5bf472626d4ef to your computer and use it in GitHub Desktop.
Preview of QueryFactory, CRUD Security and FLS in Selector Layer (see comments below)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public with sharing class OpportunitiesSelector extends fflib_SObjectSelector | |
{ | |
public List<Schema.SObjectField> getSObjectFieldList() | |
{ | |
return new List<Schema.SObjectField> { | |
Opportunity.AccountId, | |
Opportunity.Amount, | |
Opportunity.CloseDate, | |
Opportunity.Description, | |
Opportunity.ExpectedRevenue, | |
Opportunity.Id, | |
Opportunity.Name, | |
Opportunity.Pricebook2Id, | |
Opportunity.Probability, | |
Opportunity.StageName, | |
Opportunity.Type, | |
Opportunity.InvoicedStatus__c, | |
Opportunity.DiscountType__c | |
}; | |
} | |
public Schema.SObjectType getSObjectType() | |
{ | |
return Opportunity.sObjectType; | |
} | |
public List<Opportunity> selectById(Set<ID> idSet) | |
{ | |
return (List<Opportunity>) selectSObjectsById(idSet); | |
} | |
public List<Opportunity> selectByIdWithProducts(Set<ID> idSet) | |
{ | |
fflib_QueryFactory opportunitiesQueryFactory = newQueryFactory(); | |
fflib_QueryFactory lineItemsQueryFactory = | |
new OpportunityLineItemsSelector(). | |
addQueryFactorySubselect(opportunitiesQueryFactory); | |
new PricebookEntriesSelector(). | |
configureQueryFactoryFields(lineItemsQueryFactory, 'PricebookEntry'); | |
new ProductsSelector(). | |
configureQueryFactoryFields(lineItemsQueryFactory, 'PricebookEntry.Product2'); | |
new PricebooksSelector(). | |
configureQueryFactoryFields(lineItemsQueryFactory, 'PricebookEntry.Pricebook2'); | |
return (List<Opportunity>) Database.query( | |
opportunitiesQueryFactory.setCondition('id in :idSet').toSOQL()); | |
} | |
public List<OpportunityInfo> selectOpportunityInfo(Set<Id> idSet) | |
{ | |
List<OpportunityInfo> opportunityInfos = new List<OpportunityInfo>(); | |
for(Opportunity opportunity : Database.query( | |
newQueryFactory(false). | |
selectField(Opportunity.Id). | |
selectField(Opportunity.StageName). | |
selectField('Account.Name'). | |
selectField('Account.AccountNumber'). | |
selectField('Account.Owner.Name'). | |
setCondition('id in :idSet'). | |
toSOQL())) | |
opportunityInfos.add(new OpportunityInfo(opportunity)); | |
return opportunityInfos; | |
} | |
public Database.QueryLocator queryLocatorReadyToInvoice() | |
{ | |
return Database.getQueryLocator( | |
newQueryFactory().setCondition('InvoicedStatus__c = \'\'Ready\'\'').toSOQL()); | |
} | |
public class OpportunityInfo | |
{ | |
private Opportunity opportunity; | |
public Id Id { get { return opportunity.Id; } } | |
public Decimal Amount { get { return opportunity.Amount; } } | |
public String Stage { get { return opportunity.StageName; } } | |
public String AccountName { get { return opportunity.Account.Name; } } | |
public String AccountNumber { get { return opportunity.Account.AccountNumber; } } | |
public String AccountOwner { get { return opportunity.Account.Owner.Name; } } | |
private OpportunityInfo(Opportunity opportunity) { this.opportunity = opportunity; } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public with sharing class OpportunitiesSelector extends fflib_SObjectSelector | |
{ | |
public List<Schema.SObjectField> getSObjectFieldList() | |
{ | |
return new List<Schema.SObjectField> { | |
Opportunity.AccountId, | |
Opportunity.Amount, | |
Opportunity.CloseDate, | |
Opportunity.Description, | |
Opportunity.ExpectedRevenue, | |
Opportunity.Id, | |
Opportunity.Name, | |
Opportunity.Pricebook2Id, | |
Opportunity.Probability, | |
Opportunity.StageName, | |
Opportunity.Type, | |
Opportunity.InvoicedStatus__c, | |
Opportunity.DiscountType__c | |
}; | |
} | |
public Schema.SObjectType getSObjectType() | |
{ | |
return Opportunity.sObjectType; | |
} | |
public List<Opportunity> selectById(Set<ID> idSet) | |
{ | |
return (List<Opportunity>) selectSObjectsById(idSet); | |
} | |
public List<Opportunity> selectByIdWithProducts(Set<ID> idSet) | |
{ | |
assertIsAccessible(); | |
OpportunityLineItemsSelector opportunityLineItemSelector = new OpportunityLineItemsSelector(); | |
PricebookEntriesSelector pricebookEntrySelector = new PricebookEntriesSelector(); | |
ProductsSelector productSelector = new ProductsSelector(); | |
PricebooksSelector pricebookSelector = new PricebooksSelector(); | |
opportunityLineItemSelector.assertIsAccessible(); | |
pricebookEntrySelector.assertIsAccessible(); | |
productSelector.assertIsAccessible(); | |
pricebookSelector.assertIsAccessible(); | |
String query = String.format( | |
'select {0}, ' + | |
'(select {3},{5},{6},{7} ' + | |
'from OpportunityLineItems ' + | |
'order by {4}) ' + | |
'from {1} ' + | |
'where id in :idSet ' + | |
'order by {2}', | |
new List<String>{ | |
getFieldListString(), | |
getSObjectName(), | |
getOrderBy(), | |
opportunityLineItemSelector.getFieldListString(), | |
opportunityLineItemSelector.getOrderBy(), | |
pricebookEntrySelector.getRelatedFieldListString('PricebookEntry'), | |
productSelector.getRelatedFieldListString('PricebookEntry.Product2'), | |
pricebookSelector.getRelatedFieldListString('PricebookEntry.Pricebook2') | |
}); | |
return (List<Opportunity>) Database.query(query); | |
} | |
public List<OpportunityInfo> selectOpportunityInfo(Set<Id> idSet) | |
{ | |
assertIsAccessible(); | |
List<OpportunityInfo> opportunityInfos = new List<OpportunityInfo>(); | |
List<String> selectFields = | |
new List<String> { | |
'Id, ', | |
'Amount', | |
'StageName', | |
'Account.Name', | |
'Account.AccountNumber', | |
'Account.Owner.Name' }; | |
for(Opportunity opportunity : Database.query( | |
String.format('select {0} from {1} where id in :idSet order by {2}', | |
new List<String> { String.join(selectFields, ','), getSObjectName() }))) | |
opportunityInfos.add(new OpportunityInfo(opportunity)); | |
return opportunityInfos; | |
} | |
public class OpportunityInfo | |
{ | |
private Opportunity opportunity; | |
public Id Id { get { return opportunity.Id; } } | |
public Decimal Amount { get { return opportunity.Amount; } } | |
public String Stage { get { return opportunity.StageName; } } | |
public String AccountName { get { return opportunity.Account.Name; } } | |
public String AccountNumber { get { return opportunity.Account.AccountNumber; } } | |
public String AccountOwner { get { return opportunity.Account.Owner.Name; } } | |
private OpportunityInfo(Opportunity opportunity) { this.opportunity = opportunity; } | |
} | |
public Database.QueryLocator queryLocatorReadyToInvoice() | |
{ | |
assertIsAccessible(); | |
return Database.getQueryLocator( | |
String.format('SELECT {0} FROM {1} WHERE InvoicedStatus__c = \'\'Ready\'\' ORDER BY {2}', | |
new List<String>{getFieldListString(),getSObjectName(),getOrderBy()})); | |
} | |
} |
This is fantastic. Makes things so much simpler. I have a project I can't wait to use this on.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
OpportunitiesSelectorBefore.cls (108 lines) - Shows the current and traditional way to implement Selector methods to encapsulate your applications query logic. With a range of example methods, from simpe to those using inner selects and relationship fields. All custom selector methods have to call assertIsAccessible to enforce CRUD (the 'R' bit anyway) in the users profile/permission set. The current Selector base class does not enforce FLS.
OpportunitiesSelectorAfter.cls (84 lines) - Shows the new proposed Selector implementation, with a new updated base class, which is backwards compatable the current style btw. The new version supports both FLS and CRUD (configurable) automatically (no need to call assertIsAccessable in each method) and gives access to the lovely new object orientated way of build queries via the fflib_QueryFactory.
Thoughts? Suggestions?