Selector Layer - cdelfattore/apex-enterprise-patterns GitHub Wiki
The selector layer is where all of the SOQL queries are stored for a given object. All standard and custom objects can have a selector class.
- Use the plural form of the object name (Accounts, Contact, Opportunities, etc.).
- End the class name with the "Selector" suffix.
AccountsSelector
ContactsSelector
OpportunitiesSelector
- Use "selectBy[fieldName]" when creating a method that selects by a specific field.
- If the select method uses more that one field [do what?]
- Use with[ChildObjectPluralFormName] when selecting child records with the parent record query.
selectById()
-
selectByIdWithContacts()
- This would belong to the AccountsSelector class.
The virtual class that needs to be extended to implement a selector class is fflib_SObjectSelector.
Calling a selector method can be done by calling the newInstance() method implemented in the selector class and chaining the method you would like to call like so:
List<Account> accountsWithContacts = AccountsSelector.newInstance().selectByLastModifiedIdWithContacts(userIds, amount);
The above example is creating a new AccountsSelector instance and setting the result of the selectBy method to a list. You can also set the results of selector class method to a domain.
IAccounts accountsDomain = Accounts.newInstance(AccountsSelector.newInstance().selectById(accountIds));
In SOQL, you can select a parent record with a list of the child records for that parent in one SOQL call.
SELECT Id, Name, (SELECT Id, FirstName, LastName FROM Contacts) FROM Account LIMIT 10;
To do this in a selector class using fflib:
public List<Account> selectByLastModifiedIdWithContacts(Set<Id> userIds, Integer recordAmount)
{
fflib_QueryFactory accountsQueryFactory = newQueryFactory();
fflib_QueryFactory contactsQueryFactory = new ContactsSelector().addQueryFactorySubselect(accountsQueryFactory);
fflib_QueryFactory accountsQueryFactory = newQueryFactory();
fflib_QueryFactory contactsQueryFactory = accountsQueryFactory.subselectQuery('Contacts')
.selectFields(new ContactsSelector().getSObjectFieldList());
return (List<Account>) Database.query(
accountsQueryFactory
.setCondition('LastModifiedById IN :userIds')
.setOrdering('LastModifiedDate', fflib_QueryFactory.SortOrder.DESCENDING)
.setLimit(recordAmount)
.toSOQL()
);
}
You may see some code examples for fflib use the following method to construct a parent/child SOQL query.
fflib_QueryFactory accountsQueryFactory = newQueryFactory();
fflib_QueryFactory contactsQueryFactory = new ContactsSelector().addQueryFactorySubselect(accountsQueryFactory);
This method works perfectly fine. You will see a debug statement saying this method is deprecated. Only problem is if you check the commit description in the Apex Enterprise Pattern public repo, you will see that it is from nine years ago. So, it hasn't been removed yet, is it really deprecated?
In cases like this be sure to always double check the fflib code documentation and the Apex Enterprise Pattern public source code repo.
See below for an example of a selector class that can be used for an account object.
/**
* @description Selector class for the Account object.
* @author cdelfattore
* @since 2024-06-03
* @group Selector
*/
public with sharing class AccountsSelector extends fflib_SObjectSelector
implements IAccountsSelector
{
/**
* @description Use this to create new instances of this class.
* @return `IAccountsSelector`
*/
public static IAccountsSelector newInstance()
{
return (IAccountsSelector) Application.Selector.newInstance(Account.SObjectType);
}
/**
* @description List of fields to be queried. Please keep in alphbetical order.
* @return `List<Schema.SObjectField>`
*/
public List<Schema.SObjectField> getSObjectFieldList()
{
return new List<Schema.SObjectField> {
Account.AnnualRevenue,
Account.Description,
Account.Id,
Account.Name,
Account.Opportunity_Total__c
};
}
public Schema.SObjectType getSObjectType()
{
return Account.SObjectType;
}
/**
* @description Select by the Account Id.
* @param accountIds
* @return `List<Account>`
*/
public List<Account> selectById(Set<Id> accountIds)
{
return (List<Account>) selectSObjectsById(accountIds);
}
/**
* @description Select by Account Id with contacts
* @param accountIds
* @return `List<Account>`
*/
public List<Account> selectByIdWithContacts(Set<Id> accountIds)
{
return (List<Account>) Database.query(
createContactsByAccountQueryFactory()
.setCondition('Id IN :accountIds')
.toSOQL()
);
}
/**
* @description Select by the LastModifiedId, OrderByLastModifiedDate Desc, with related Contacts.
* @param userIds
* @param recordAmount
* @return `List<Account>`
*/
public List<Account> selectByLastModifiedIdWithContacts(Set<Id> userIds, Integer recordAmount)
{
return (List<Account>) Database.query(
createContactsByAccountQueryFactory()
.setCondition('LastModifiedById IN :userIds')
.setOrdering('LastModifiedDate', fflib_QueryFactory.SortOrder.DESCENDING)
.setLimit(recordAmount)
.toSOQL()
);
}
/**
* @description This query factory is created in a few different methods. Let's use this private method to construct the ContactByAccount query factory.
* @return `fflib_QueryFactory`
*/
private fflib_QueryFactory createContactsByAccountQueryFactory()
{
fflib_QueryFactory accountsQueryFactory = newQueryFactory();
fflib_QueryFactory contactsQueryFactory = new ContactsSelector().addQueryFactorySubselect(accountsQueryFactory);
return accountsQueryFactory;
}
}
Most up to date version of this class can be found (here)[https://github.com/cdelfattore/apex-enterprise-patterns/blob/master/force-app/main/default/classes/AccountsSelector.cls].
The implemented interface
/**
* @description Interface for the Account Selector class.
* @author cdelfattore
* @since 2024-06-03
*/
public interface IAccountsSelector
{
List<Account> selectById(Set<Id> idSet);
List<Account> selectByIdWithContacts(Set<Id> accountIds);
List<Account> selectByLastModifiedIdWithContacts(Set<Id> userIds, Integer recordAmount);
}
Most up to date version of this interface can be found (here)[https://github.com/cdelfattore/apex-enterprise-patterns/blob/master/force-app/main/default/classes/IAccountsSelector.cls].
Additional examples can be found is this repo and at Apex Enterprise Pattern Github repositories.