Skip to content

Instantly share code, notes, and snippets.

@afawcett
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afawcett/659211d5bf472626d4ef to your computer and use it in GitHub Desktop.
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)
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; }
}
}
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()}));
}
}
@afawcett
Copy link
Author

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?

@wes1278
Copy link

wes1278 commented Jun 24, 2014

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