Implementing the standard query logic

The previous Selector usage example required a cast of the list returned to a list of Race__c objects, which is not ideal. To improve this, you can easily add a new method to the class to provide a more specific version of the base class method, as follows:

public List<Race__c> selectById(Set<Id>raceIds){
  return (List<Race__c>) selectSObjectsById(raceIds);
}

Thus, the usage code now looks like this:

List<Race__c> races = 
  new RacesSelector().selectById(raceIds); 

Standard features of the Selector base class

The fflib_SObjectSelector base class contains additional functionality to provide more query consistency and integration with the platform. These apply to the aforementioned selectSObjectsById method as well as your own. The following sections highlight each of these features.

Tip

You can, of course, extend the standard features of this base class further. Perhaps there is something you want all your queries to consider, a common field or aspect to your schema design, or a feature that is common to most of your selectors. In this case, create your own base class and have your selectors extend that, as follows.

public abstract class ApplicationSelector
   extends fflib_SObjectSelector
{
  // Add your common methods here
}
public class RacesSelector
   extends ApplicationSelector
{
  // Methods using methods from both base classes
}

Enforcing object and field security

Salesforce requires developers to implement the object read security and field level read security checks when querying. Typically using the methods on the SObjectDescribe and SObjectFieldDescribe Apex runtime types. The following sections describe the fflib_SObjectSelector base class support in respect to this.

Default behavior

By default, the base class methods in the fflib_SObjectSelector base class automatically perform Object read security. The ability of the base class to enforce Field level read security is also available but is not enforced by default.

In both cases, if enforcement is enabled and user object or field level read permissions have not been granted for the object or fields referenced in the getSObjectType and getSObjectFieldSetList methods, an exception will be thrown.

Overriding the default behavior

You may wish to disable the aforementioned default enforcement in cases where objects are read by the application code on behalf of the user. For example, reading the race data to update other areas of the application on behalf of the user. Such access is often referred to as system level access. In these cases, you may not wish to have this check enforced.

Allowing this enforcement check to execute in some cases may not be desirable since, as given in the preceding example, it then requires users to be granted access to the underlying race data object in order to use aspects of the application that read but do not display race data to the user. Another motivation for disabling the selector default behavior would be a preference to code these checks in your controller logic more explicitly.

Constructor parameters provided by the SObjectSelector class allow each Selector class to control whether security enforcement is enabled or not. This is also available to any code instantiating a Selector class, allowing this decision to be made on a per use case basis, for example, when the selector is used within a Controller class.

To make it easier to configure the fflib_SObjectSelector class throughout the application as well as to provide a means to establish a place for shared or common Selector logic code, a new base class can be created as follows:

public abstract class ApplicationSelector
  extends fflib_SObjectSelector {

    public ApplicationSelector() {
        this(false);
    }

    public ApplicationSelector(Boolean includeFieldSetFields) {
        // Disable the default base class read security checking 
        //  in preference to explicit checking elsewhere        
        this(includeFieldSetFields, false, false);
    }

    public ApplicationSelector(
        Boolean includeFieldSetFields, 
        Boolean enforceCRUD, 
        Boolean enforceFLS) {
        // Disable sorting of selected fields to aid debugging//  (performance optimisation)
        super(
          includeFieldSetFields, enforceCRUD, enforceFLS, false);
    }
}

Tip

The preceding sample also leverages another configuration parameter of the fflib_SObjectSelector base class, the sortSelectFields parameter. This has been added to allow disablement of the sorting of the field names while building the SOQL queries. While this can aid debugging, it does have a performance overhead. For backwards compatibility the base class retains this behavior, but allows for it to be disabled optionally.

Thus Selector classes in this application extended this class instead:

public class RacesSelector extends ApplicationSelector {

Note

Note that this approach requires that the developer takes over exclusive responsibility for implementing object and field level security checking themselves. This will be discussed in further detail a later chapter.

Ordering

As mentioned in the conventions, having a default order to records is important to avoid the random non-deterministic behavior of the platform SOQL engine. The selectsObjectById base class method calls the getOrderBy method to determine which field(s) to use to order the records returned from the Selector.

The default behavior is to use the Name field (if available, otherwise CreatedByDate is used). As such, the previous usage is already ordering by Name without further coding. If you wish to change this, override the method as follows. This example is used by the ContestantsSelector class to ensure that contestants are always queried in the order of season, race, and their race position:

public override String getOrderBy() {
  return 'Race__r.Season__r.Name, Race__r.Name, RacePosition__c';
}

Later in this chapter, you will see how custom Selector methods can be constructed; these have the option of using the getOrderBy method. This allows some methods to use a different order by clause other than the default if you wish to do so.

Field Sets

As was discussed in a previous chapter, utilization of the Field Sets platform feature is the key to ensuring that your application is strongly aligned with the customization capabilities that your application's users expect. However, you might be wondering what it has to do with querying data? Well, even though most of our discussions around this feature will focus on the use of it in a UI context, the additional custom fields added in the subscriber org still need to be queried for them to be displayed; otherwise, an exception will occur just the same as if you had failed to query a packaged field.

To use this feature, you need to override the getSObjectFieldSetList method and construct the Selector with the includeFieldSetFields parameter. The default constructor sets this parameter to false so that in general, the Field Sets fields are not included. The following example assumes a field set on the Race object called SeasonOverview (included in the code of this chapter):

Field Sets

The following code illustrates the configuration of this feature (note that the logic in the base class also ensures that if the subscriber adds your own packaged fields to the Field Set, such fields are only added to the SOQL statement field list once):

public RacesSelector() {
  super();
}

public RacesSelector(Boolean includeFieldSetFields) {
  super(includeFieldSetFields);
}

public override List<Schema.FieldSet>getSObjectFieldSetList() {
  return new List<Schema.FieldSet>
     { SObjectType.Race__c.FieldSets.SeasonOverview };
}

Multi-Currency

As discussed in Chapter 2, Leveraging Platform Features, enabling and referencing certain platform features can create dependencies on your package that require your customers to also enable those features prior to installation, and this might not always be desirable. One such feature is Multi-Currency. Once this is enabled, every object gains a CurrencyISOCode field; it then also appears on layouts, reports, and so on. Salesforce ensures that subscribers are fully aware of the implications before enabling it.

If you want to leverage it in your application (but do not think it will be something that all your customers will require), you have the option to reference this field using Dynamic Apex and Dynamic SOQL. It is in this latter area that the fflib_SObjectSelector base class provides some assistance.

Tip

You might want to consider the approach described here when working with Personal Accounts, as certain fields on the Account object are only present if this feature is enabled.

If you explicitly listed the CurrencyISOCode field in your getSObjectFieldList method, you will therefore bind your code and the package that contains it to this platform feature. Instead, what the base class does is use the UserInfo.isMultiCurrencyOrganization method to dynamically include this field in the SOQL query that it executes. Note that you still need to utilize Dynamic Apex to retrieve the field value, as shown in the following code:

List<Team__c> teams = (List<Team__c>) 
   new TeamsSelector().selectSObjectsById(teamIds);
for(Team__c team : teams) {
   String teamCurrency = 
      UserInfo.isMultiCurrencyOrganization() ?
        (String) team.get('CurrencyIsoCode') :
        UserInfo.getDefaultCurrency();
}

For Domain logic, you might want to put a common method in your own Domain base class (such as the ApplicationDomain class described in the last chapter). This way, you can encapsulate the preceding turnery operation into a single method. The sample code for this chapter contains the ApplicationDomain.getCurrencyCodes method, which can be used as follows:

public override onBeforeInsert() {
  Map<Id, String> currencies = getCurrencyCodes();
  for(Team__c team : (List<Team__c>) Records) { 
    String teamCurrency = currencies.get(team.Id);
  }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.141.219