15

I have

Schema.SObjectField field = MyObject__c.myField__c;
Schema.SObjectType objectType = getTypeOf(field);

how would getTypeOfField() need to look like for this to be true?

System.assertEquals(MyObject__c.SObjectType, objectType);
11

I may be missing the boat here but why not a method based on the following?

// Build a map of hashcodes for each fieldDescribe taken from Schema Global Describe
Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
Map<Integer,Schema.SObjectType> fldHashCodeToSObjectTypeMap = new map<Integer,Schema.SObjectType>();

for (String sobjname: gd.keySet()) {
    for (Schema.SObjectField sobjField : gd.get(sObjName).getDescribe().fields.getMap().values())
        fldHashCodeToSObjectTypeMap.put(((Object) sObjField).hashCode(),gd.get(sobjName));

}

// Then compare your sobjectField in hand by hashing it and then looking up into the map
Schema.SObjectField field0 = Account.Industry; 
Integer field0Hash = ((Object) field0).hashCode();
Schema.SObjectField field1 = Opportunity.CloseDate; 
Integer field1Hash = ((Object) field1).hashCode();

System.debug(LoggingLevel.INFO,'field0 belongs to sobject:' + fldHashCodeToSObjectTypeMap.get(field0Hash)); 
System.debug(LoggingLevel.INFO,'field1 belongs to sobject:' + fldHashCodeToSObjectTypeMap.get(field1Hash));

Debug output yields:

17:58:06.113 (1113461278)|USER_DEBUG|[16]|INFO|field0 belongs to sobject:Account
17:58:06.113 (1113547114)|USER_DEBUG|[17]|INFO|field1 belongs to sobject:Opportunity

Your Util.getType(someSobjectField) method would simply do

fldHashCodeToSObjectTypeMap.get((Object) someSobjectField).hashCode());

on some static variable instantiation of fldHashCodeToSObjectTypeMap

  • hashCode func is an interesting angle - looks good! – bigassforce Jan 14 '16 at 2:24
  • @bigassforce : this is what happens when one reads too many SFSE answers from smarter folks than me; odd ideas show up in my brain – cropredy Jan 14 '16 at 2:28
  • I think I don't really like this because what you do is not asking a encapsulated system for an answer bu store all possible answers in a map before and than just retrieve them. – Robert Sösemann Jan 15 '16 at 15:34
  • oh well. At least your 50 pt incentive inspired me to learn something new. – cropredy Jan 15 '16 at 16:02
  • 1
    Sure @crop1645 thanks for sharing your idea. It's better than leaving it unsolved. But actually I expected some Salesforce internals to tell me there is an undocumented method getSObjectType() on SObjectField ;-) – Robert Sösemann Jan 15 '16 at 18:58
8
+50

While I really hope there is an easier way to achieve this, the Tooling API might be one candidate.

You can interrogate the Salesforce Tooling API with an Apex library or just use the code below by itself:

  1. First specify your field name:

    //choose your field name (without suffix!)
    String fieldName = yourField.getDescribe().getName().substringBefore('__c');
    System.debug(fieldName);
    

    'Status'

  2. Then obtain the Custom Object Id (eg 01I...) or Table Enum (eg Account) which it lives on:

    String query = 'SELECT TableEnumOrId From CustomField WHERE DeveloperName = \'' + fieldName + '\'';
    String endpoint = Url.getSalesforceBaseUrl().toExternalForm();
    endpoint += '/services/data/v29.0/tooling/query/?q=';
    endpoint += EncodingUtil.urlEncode(query, 'UTF-8');
    
    HttpRequest request = new HttpRequest();
    request.setMethod('GET');
    request.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
    request.setEndpoint(endpoint);
    
    HttpResponse response = new Http().send(request);
    String tableEnumOrId = response.getBody().substringAfter('"TableEnumOrId":"').substringBefore('"');
    
    System.debug(tableEnumOrId);
    

    '01Ib0000000CM8TEAW'

  3. Finally, you can pull the API / Developer Name off the Custom Object definition itself:

    query = 'SELECT DeveloperName From CustomObject Where Id = \'' + tableEnumOrId + '\'';
    endpoint = Url.getSalesforceBaseUrl().toExternalForm();
    endpoint += '/services/data/v29.0/tooling/query/?q=';
    endpoint += EncodingUtil.urlEncode(query, 'UTF-8');
    
    request = new HttpRequest();
    request.setMethod('GET');
    request.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
    request.setEndpoint(endpoint);
    
    response = new Http().send(request);
    System.debug(response.getBody());
    String customObjectName = response.getBody().substringAfter('"FullName":"').substringBefore('"');
    
    System.debug(customObjectName);
    

    'Schematic'

  4. Use Type.forName to get at your desired SObject, SObjectType, or DescribeSObjectResult:

    Type reflector = Type.forName('Invoice__c');
    SObject obj = (SObject)reflector.newInstance();
    SObjectType objectType = obj.getSObjectType();
    //now you can do objectType.getDescribe etc...
    

There are a number of caveats which would need serious 'hardening':

  • you may need to truncate or append the __c as necessary in the Tooling API queries
  • in managed context you will need to play with the namespace prefix.
  • gonna be tricky if you have multiple fields with the same name!
  • 4
    Just wondering how this would work if i had the same field name on multiple custom objects? e.g. Total on object A and object B? – Andrew Fawcett Apr 11 '14 at 15:00
  • Wouldn't work, thanks for spotting! Need to query TableEnumOrId from CustomObject beforehand @AndrewFawcett – bigassforce Oct 17 '17 at 12:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.