| name | soql-lib-selector |
| description | Creates Salesforce Apex selector classes using the SOQL Lib selector pattern. Covers class declaration (extends SOQL, implements SOQL.Selector), constructor defaults, static query() factory, domain-specific filter methods, naming conventions (SOQL_<ObjectName>), and call-site patterns. Use when creating a new selector class, adding filter methods to an existing selector, or when asked how to encapsulate SOQL queries in a reusable class. |
SOQL Lib — Selector Pattern
A selector is a dedicated Apex class that encapsulates all queries for one SObject. It extends SOQL, implements SOQL.Selector, and exposes domain-specific filter methods.
When to Use
- Use this skill when creating a new selector class for any SObject
- Use this skill when adding or refactoring filter methods on an existing selector
- Use this skill when asked how to structure or call a SOQL Lib query class
Instructions
Class structure rules
| Rule | Value |
|---|
| Class name | SOQL_<ObjectApiName> |
| Sharing keyword | inherited sharing |
| Extends | SOQL |
| Implements | SOQL.Selector |
| Constructor visibility | private |
| Mock ID constant | @TestVisible private static final String MOCK_ID = 'SOQL_<ClassName>' |
| Filter method return type | The selector class (e.g. SOQL_Account), not Queryable |
| Default security | .systemMode().withoutSharing() in constructor |
Constructor checklist
private SOQL_Account() {
super(Account.SObjectType); // 1. required
with(Account.Id, Account.Name); // 2. default SELECT fields
systemMode(); // 3. ignore FLS in selector layer
withoutSharing(); // 4. sharing enforced at service layer
mockId(MOCK_ID); // 5. enable unit test mocking
}
Filter method patterns
// Equality
public SOQL_Account byType(String type) {
whereAre(Filter.with(Account.Type).equal(type));
return this;
}
// Null-safe — filter skipped when value is null
public SOQL_Account byIndustry(String industry) {
whereAre(Filter.with(Account.Industry).equal(industry).ignoreWhen(industry == null));
return this;
}
// IN
public SOQL_Account byOwnerIds(Set<Id> ownerIds) {
whereAre(Filter.with(Account.OwnerId).isIn(ownerIds));
return this;
}
// Boolean
public SOQL_Account activeOnly() {
whereAre(Filter.with(Account.IsActive__c).isTrue());
return this;
}
// Date literal
public SOQL_Opportunity closingThisMonth() {
whereAre(Filter.with(Opportunity.CloseDate).equal('THIS_MONTH').asDateLiteral());
return this;
}
// Range
public SOQL_Opportunity aboveAmount(Decimal minAmount) {
whereAre(Filter.with(Opportunity.Amount).greaterOrEqual(minAmount));
return this;
}
### Calling the selector
Selector filter methods always should be called first, then SOQL Lib overrides.
```apex
// Basic
List<Account> accounts = SOQL_Account.query().toList();
// Chained filters
List<Account> accounts = SOQL_Account.query()
.byType('Customer')
.byIndustry('Technology')
.with(Account.Phone)
.toList();
// Add extra fields beyond constructor defaults
List<Account> accounts = SOQL_Account.query()
.with(Account.BillingCity, Account.Phone)
.toList();
// Override security ad-hoc (e.g. from a controller)
List<Account> accounts = SOQL_Account.query()
.userMode()
.toList();
// Top-level OR logic between selector filters
List<Account> accounts = SOQL_Account.query()
.byIndustry('IT')
.byRecordType('Partner')
.anyConditionMatching()
.toList();
// Map result
Map<Id, Account> accountMap = (Map<Id, Account>) SOQL_Account.query()
.byIds(accountIds)
.toMap();
// Single record (null-safe)
Account acc = (Account) SOQL_Account.query()
.byId(accountId)
.toObject();
// Extract single field value
String industry = (String) SOQL_Account.query()
.byId(accountId)
.toValueOf(Account.Industry);
// Extract IDs of a related field
Set<Id> ownerIds = SOQL_Account.query().toIdsOf(Account.OwnerId);
Examples
Minimal selector
public inherited sharing class SOQL_Account extends SOQL implements SOQL.Selector {
@TestVisible
private static final String MOCK_ID = 'SOQL_Account';
public static SOQL_Account query() {
return new SOQL_Account();
}
private SOQL_Account() {
super(Account.SObjectType);
with(Account.Id, Account.Name);
systemMode();
withoutSharing();
mockId(MOCK_ID);
}
public SOQL_Account byType(String type) {
whereAre(Filter.with(Account.Type).equal(type));
return this;
}
public SOQL_Account byOwnerId(Id ownerId) {
whereAre(Filter.with(Account.OwnerId).equal(ownerId));
return this;
}
}
Complete selector
public inherited sharing class SOQL_Opportunity extends SOQL implements SOQL.Selector {
@TestVisible
private static final String MOCK_ID = 'SOQL_Opportunity';
public static SOQL_Opportunity query() {
return new SOQL_Opportunity();
}
private SOQL_Opportunity() {
super(Opportunity.SObjectType);
with(Opportunity.Id, Opportunity.Name, Opportunity.StageName, Opportunity.CloseDate, Opportunity.Amount);
systemMode();
withoutSharing();
mockId(MOCK_ID);
}
public SOQL_Opportunity byStageName(String stageName) {
whereAre(Filter.with(Opportunity.StageName).equal(stageName));
return this;
}
public SOQL_Opportunity byAccountId(Id accountId) {
whereAre(Filter.with(Opportunity.AccountId).equal(accountId).ignoreWhen(accountId == null));
return this;
}
public SOQL_Opportunity byAccountIds(Set<Id> accountIds) {
whereAre(Filter.with(Opportunity.AccountId).isIn(accountIds));
return this;
}
public SOQL_Opportunity closingThisMonth() {
whereAre(Filter.with(Opportunity.CloseDate).equal('THIS_MONTH').asDateLiteral());
return this;
}
public SOQL_Opportunity isOpen() {
whereAre(Filter.with(Opportunity.IsClosed).isFalse());
return this;
}
public SOQL_Opportunity aboveAmount(Decimal minAmount) {
whereAre(Filter.with(Opportunity.Amount).greaterOrEqual(minAmount));
return this;
}
}
Selector with parent fields
public inherited sharing class SOQL_Contact extends SOQL implements SOQL.Selector {
@TestVisible
private static final String MOCK_ID = 'SOQL_Contact';
public static SOQL_Contact query() {
return new SOQL_Contact();
}
private SOQL_Contact() {
super(Contact.SObjectType);
with(Contact.Id, Contact.FirstName, Contact.LastName, Contact.Email);
with('Account', Account.Name, Account.Industry);
systemMode();
withoutSharing();
mockId(MOCK_ID);
}
public SOQL_Contact byAccountId(Id accountId) {
whereAre(Filter.with(Contact.AccountId).equal(accountId));
return this;
}
public SOQL_Contact byRecordTypeName(String devName) {
byRecordType(devName);
return this;
}
}
Usage in a controller (with sharing, userMode)
public with sharing class ExampleController {
@AuraEnabled
public static List<Contact> getContactsByRecordType(String recordType) {
return SOQL_Contact.query()
.byRecordType(recordType)
.with(Contact.Email, Contact.Title)
.toList();
}
@AuraEnabled
public static List<Account> getAccountsByRecordTypeOrIndustry(String recordType) {
return SOQL_Account.query()
.byIndustry('IT')
.byRecordType(recordType)
.anyConditionMatching()
.with(Account.Industry, Account.AccountSource)
.toList();
}
@AuraEnabled
public static String getAccountIndustry(Id accountId) {
return (String) SOQL_Account.query()
.byId(accountId)
.toValueOf(Account.Industry);
}
}