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);
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It only takes a minute to sign up.
Sign up to join this communityAs of Spring 23, this can be solved using this method getSObjectType()
on class DescribeFieldResult
Schema.DescribeFieldResult fieldDescribe = someField.getDescribe();
Schema.sObjectType sourceType = fieldDescribe.getSObjectType();
or, in one line
Schema.sObjectType sourceType = someField.getDescribe().getSObjectType();
e.g.
Assert.areEqual(Account.SObjectType,Account.Website.getDescribe().getSObjectType());
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
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:
First specify your field name:
//choose your field name (without suffix!)
String fieldName = yourField.getDescribe().getName().substringBefore('__c');
System.debug(fieldName);
'Status'
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'
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'
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':
__c
as necessary in the Tooling API queriesTableEnumOrId
from CustomObject beforehand @AndrewFawcett
Oct 17, 2017 at 12:42
These are just two additional hacks to get the information until SF finally provides a propper getter. The first is faster, the second is solid.
SF obviously stores the information on the field internally and use it for the Error message:
System.SObjectException: MyNamespace__MyCustomObject__c.Name does not belong to SObject type Account
I decided to simply use this information. I know it's a little fragile since we never know what they will change internally, but since we don't have any stable, lean alternative and I don't want to do API callouts for simple information like this, I decided to go this way.
private static final Map<String, SObjectType> SOBJECT_TYPES = Schema.getGlobalDescribe();
private final SObjectField field;
public SObjectType sObjectType() {
try {
new Account().put(field, null);
return Account.SObjectType;
}
catch (SObjectException e) {
return SOBJECT_TYPES.get(e.getMessage().substringBefore('.'));
}
}
It works for custom, standard, and namespaced types. I also did some performance testing and can say, that it takes about twice as long as getting the lookups parent type as we all do it sometimes something similar to this: field.getDescribe().getReferenceTo()[0]
.
Another approach is a roundtrip through the parent type. There we know all the child relationships, so what we need to do, is find the child relationship, that belongs to this field. And this child relationship now also knows the child's Type.
public SObjectType sObjectTypeStable() {
for(ChildRelationship relationship : parentType().getDescribe().getChildRelationships()) {
if(relationship.getField() == field) {
return relationship.getChildSObject();
}
}
return null;
}
Compared to the try/catch approach it multiplies the execution time by a factor of 10 or more (probably varies by the ChildReference's index), so I do not recommend it!
This gist contains the example class. For sure, it could be performance-optimized, but in real-world scenarios, I would not benefit from any additional caching so I skipped it.
This is how its used:
SObjectField field = Contact.AccountId;
SObjectType type = new FieldDescribe(field).sObjectType();
SObjectType stable = new FieldDescribe(field).sObjectTypeStable();
SObjectType parent = new FieldDescribe(field).parentType();
List<SObjectType> parents = new FieldDescribe(field).parentTypes();
I know this is a little old, but I was searching how to solve this problem and I couldn't find anything useful. This is how I solved it:
Type myObjectType = MyObject__c.myField__c.class;
String myObjectName = myObjectType.toString(); // You can use getName() as well
That code will give you the Type of MyObject__c
class. Then you can use all the methods for Type
class in Apex like toString()
or getName()
to get the String name of MyObject__c
.
Please check the Salesforce Type Class for more info.
getReferenceTo()
Schema.SObjectField
? If you could start passing fieldsets around you might gain some flexibility (admin able to modify the page without the help of a developer) and the FieldSet class does have thegetSobjectType()
: salesforce.com/us/developer/docs/apexcode/Content/…