apex-enterprise-patterns / fflib-apex-common

Common Apex Library supporting Apex Enterprise Patterns and much more!
BSD 3-Clause "New" or "Revised" License
913 stars 517 forks source link

How to Use FieldSets with Application.Selector.newInstance? #283

Open Daniel-Dragon opened 4 years ago

Daniel-Dragon commented 4 years ago

Hello, I have more of a discussion question regarding how to enable FieldSets to be more flexible.

daveespo commented 4 years ago

I'm not entirely sure I understand your first bullet point -- Can you provide an example of that usage? The most common usage of fflib_SObjectSelector is to subclass it for your own object and define the list of field sets you want to query by overriding the virtual getSObjectFieldSetList()

The purpose for having multiple Field Sets usually becomes evident if you need multiple Selectors -- i.e. a 'get me the kitchen sink' Selector that enumerates all fields and perhaps a more narrow Selector where heap size or some other consideration (FLS) makes it convoluted to use a single kitchen sink Selector.

Daniel-Dragon commented 4 years ago

Please keep in mind that this could be a serious misunderstanding on my part. I'm slowly piecing together how this is supposed to work and reading the book! So my assumption is that whenever we need the Selector "injected" anywhere we want to use Application.Selector.newInstance, this will allow us to inject a mock when testing, and allow us to swap out implementations if they change.

So with that assumption, what I am saying is If I'm using the Application static class to get the selector right? Say Account object so I have an AccountsSelector class, and it's registered in my Application class.

So now I'm supposed to use Application.Selector.newInstance(Account.SObjectType) when I want to inject it? (If that part is wrong then my whole question is wrong) What if I want to inject the "Kitchen Sink" this time?

stohn777 commented 4 years ago

All of that is right on, and as long as the "Kitchen Sink" implements fflib_ISObjectSelector, you'll be fine.

Additionally the selector implementation could contain a static newInstance() method that calls Application for you, so the usage might look like: List<KitchenSink__c> sinkList = KitchenSinkSelector.newInstance().selectSinksByVolume(...); See the sample usage project, where the AccountsSelector [1] has it.

Welcome to AEP, and I look forward to hearing about your experiences and perhaps some suggestions.

[1] https://github.com/apex-enterprise-patterns/fflib-apex-common-samplecode/blob/master/sfdx-source/apex-common-samplecode/main/classes/selectors/AccountsSelector.cls

Daniel-Dragon commented 4 years ago

Ah this is perfect thank you! I knew I saw this somewhere. OK, so that leaves the last point of question still open I think which is the FieldSet itself.

So I pulled the example here from Andrew Fawcett's book. The snippet below is extending fflib_SObjectSelector and is intended to be extended further with an actual selector class to give context.

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);
 }
}

These constructors seem to give us the flexibility to includeFieldSetFields, enforceCRUD, enforceFLS, ETC Right? How can we leverage these constructors (To get the kitchen sink implementation by saying includeFieldSetField = true) when using an implementation of the class though, how can I call one of these constructors? Without explicitly defining the class that I'm creating of course! Because if I do that then I lose the ability to Mock.

ImJohnMDaniel commented 4 years ago

G'day @Dan-Baba

"These constructors seem to give us the flexibility to includeFieldSetFields, enforceCRUD, enforceFLS, ETC Right? "

"How can we leverage these constructors (To get the kitchen sink implementation by saying includeFieldSetField = true) when using an implementation of the class though how can I call one of these constructors? Without explicitly defining the class that I'm creating of course! Because if I do that then I lose the ability to Mock."

Trust this helps.

NMoore42 commented 4 months ago

Daniel-Dragon did you ever determine how to do this? I am in a similar boat as you - as of now, my solution is to define overloaded newInstance methods in the selector:

public static ChangeRequestsSelector newInstance() {
    return newInstance(false, false);
}

public static ChangeRequestsSelector newInstance(Boolean runInSystemMode) {
    return newInstance(runInSystemMode, false);
}

public static ChangeRequestsSelector newInstance(Boolean runInSystemMode, Boolean includeFieldSetFields) {
    ChangeRequestsSelector selector = (ChangeRequestsSelector) Application.selector.newInstance(Change_Request__c.SobjectType);
    selector.setDataAccess(runInSystemMode ? fflib_SObjectSelector.DataAccess.SYSTEM_MODE : fflib_SObjectSelector.DataAccess.USER_MODE);
    if (includeFieldSetFields) {
        selector.includeFieldSetFields();
    }
    return selector;
}