Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Daniel-Dragon opened this issue Jun 2, 2020 · 6 comments
Open

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

Daniel-Dragon opened this issue Jun 2, 2020 · 6 comments

Comments

@Daniel-Dragon
Copy link

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

  • If you're using Application.Selector.newInstance how do you provide the parameter for adding the fieldsets?
  • Is there a point to having multiple FieldSets? Or do most people just created like a "Default" fieldset that has extended fields to query for?
@daveespo
Copy link
Contributor

daveespo commented Jun 3, 2020

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
Copy link
Author

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
Copy link
Contributor

stohn777 commented Jun 5, 2020

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
Copy link
Author

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
Copy link
Contributor

G'day @Dan-Baba

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

  • Yes, that is correct

"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."

  • So first of all, if you have not already review the sample code, take a look at the Application.cls starting at line 52. This is a typical example of the Application class setup in a non-DX environment. The AT4DX version of the Application.cls starting at line 40 is what you would use if you have a DX project with multiple second generation packages.
  • In both cases, the methods are not exposed to activate the extra features. Having said that, the AT4DX version is based on the ApplicationSObjectSelector which is itself based on the fflib_SObjectSelector. The ApplicationSObjectSelector does enable field sets as it is part of the AT4DX fieldset injection functionality, so that might help you.
  • In both cases, the Application classes have a series of setMock methods to help you take advantage of that functionality. Look in the fflib-apex-common-samplecode and the at4dx-samplecode projects for examples of its usage.

Trust this helps.

@NMoore42
Copy link

NMoore42 commented Jun 12, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants