apex-enterprise-patterns / fflib-apex-common

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

Can't select relationship fields in the SObjectFieldList #198

Closed Xtremefaith closed 4 years ago

Xtremefaith commented 6 years ago

I have a Selector class and on the getSObjectFieldList() I'm trying to traverse some relationship fields like so:

public List<Schema.SObjectField> getSObjectFieldList() {
    return new List<Schema.SObjectField> {
        Location__c.Id,
        Location__c.Name,      
        Location__c.Site__r.State__c
    };
}

Unfortunately this results in an error when I attempt to deploy it:

Variable does not exist: State__c

Is this something not possible? Could it be made possible? Can I still add a nested field to the query at a later time like so?

fflib_QueryFactory query = newQueryFactory();
query.selectField(Location__c.Site__r.State__c);

I will try to provide more details as I find them, but I'm hoping to deploy this tonight. Also I came across this forum response that I thought might be related

foxysolutions commented 6 years ago

@Xtremefaith it is indeed possible to add a relational field, but not by using the setFields by SObjectField. Please try to apply this using Strings, as the example below provides: query.selectField( 'Site__r.State__c' );

Another option, in case of having multiple relationship fields (or fields you don't want to query for each call of that object, but only for that specific selector method), is to use a List of Strings which refers to the (relational) field names.

fflib_QueryFactory query = newQueryFactory(); 
query.selectField( new List<String>{ 'Id', 'Name', 'Site__r.State__c' } );

The options are endless :). Hope you already figured out how to resolve this, but please shoot in case of any open comments or questions.

afawcett commented 4 years ago

@foxcreation thanks so much for answer this! Great answer. @Xtremefaith also hope you got sorted.

Sahityatarumani commented 3 years ago

@foxysolutions How do I fetch these fields in selectById method? Should I customize the default implementation (which is to return results from selectSObjectsById) to use queryFactory?

Pretty old post. Hope you see this. :)

wimvelzeboer commented 3 years ago

Hi @Sahityatarumani, I would indeed use the queryfactory in that case instead of selectSObjectById

foxysolutions commented 3 years ago

@Sahityatarumani when deviating from the standard implementation, it is indeed best/easiest to create a separate selector method, using the QueryFactory and then specifying all details there, as @wimvelzeboer already mentioned. Nevertheless, in case this applies to you for multiple different objects (selectors), you might also think of adding a 'getRelationshipFields()' method in the fflib_SObjectSelector class which can be overwritten by any implementation of it and then customise the configureQueryFactory() method for by default adding relationship fields when provided. That however is up to your implementation needs whether it is more beneficial than customizing this one object.

Sahityatarumani commented 3 years ago

Thanks a lot @wimvelzeboer & @foxysolutions. I ended up using the queryFactory. My use case spanned only 2 objects for now. However, I do like the idea of adding an abstraction in fflib_SObjectSelector! I will look into this further.

Thanks again! I learn so much from these posts!

ImJohnMDaniel commented 3 years ago

@Sahityatarumani @foxysolutions and @wimvelzeboer -- When you need to include the fields from another SObject to a query, you would indeed create a selector for that other object and then use either the fflib_QueryFactory's configureQueryFactoryFields() method if you want to add the fields from a parent object or you would use the ffib_QueryFactory's addQueryFactorySubselect() method if you wanted to add a subselect. This allows each SObject's selector to honor the "single responsibility" principle and still connect the releationships.

The OpportunitiesSelector's selectByIdWithProducts(Set<Id>) method in the fflib-apex-common-samplecode project is a great example of this in use.

bobalicious commented 3 years ago

If that field is needed in your base query (as the reference to getSObjectFieldList implies in the original question), it might be simpler to create a formula field instead.

We don't always need to code round problems - we have declarative available too!

foxysolutions commented 3 years ago

@bobalicious completely agree a formula field might simplify the solution, however please be aware that each time a record is retrieved by Salesforce (e.g. in Trigger context), all formula fields are calculated, and might have an impact on your performance.

While I'm a big fan of declarative solutions, I would personally say adding a formula field purely to avoid creating a custom selector method doesn't weigh the 'costs' of the formula field. Unless that formula field is used on layouts and reports, or when you want to use Flows for retrieving (although also in Flow you can get a parent record field without a formula).

When it is used in other places than only in the getSObjectFieldList I completely agree that might be an approach as well.