I've got a component which is used on multiple object types in their visualforce pages. I have a controller which provides some data, and another to add some relevant fields.

The controller which adds some fields, now needs to conditionally add a field, based solely on the object type. Seems pretty simple, right? Lets just check the type of the record from getRecord() - wait, doing that causes a Cannot Modify Record before calling addFields() error. How about getId()? Works great, except for having to cast the return (a string) to an id. Oh wait, nope! It doesn't work if the record doesn't have an id.

Now I'm kind of stuck - how do I tell the object type of a record without accessing it, or its id? I don't see any methods for accessing this data in the standard controller class.

I've considered some funky methods, such as providing this data via attributes to the controller or trying to serialize the controller/record and parse the result sniffing for types, but these would access the record before calling getFields. In theory I could rely less on the field and use a fieldset instead, but I still need to know what Object in working with.

Heres some sample code:

public class FieldProvider {
    public FieldProvider(ApexPages.StandardController controller) {
            List<String> fields = new List<String>{

            // Fails if Id is null 
            // Can't use `getRecord` before calling `addFields`
            if (Id.valueOf(controller.getId()).getsObjectType().getDescribe().fields.getMap().containsKey('Conditional__c')) {

            if (!Test.IsRunningTest()) { controller.addFields(fields); }

Tl;dr: I need to identify the type of this record, so I can tell if this field exists on the given object type. But, I can't access the record or its id.

  • 1
    Yeah with an Id, it's simple, but without, not so much. Why not just move everything to dynamic SOQL? Is that an option? – Adrian Larson Dec 5 '18 at 17:48
  • I don't think so, there's two other extensions using the same pattern on the page. Plus it has to use the standard controller, otherwise I would have replaced the separate controllers with a single one already. – battery.cord Dec 5 '18 at 17:52
  • 1
    You don't really need the field to be queried when it's new, though. Or do you get an exception in that case? Can you just do your Id check in the case where getId() returns a value? Calling addFields should essentially do nothing when you pass in a record not yet saved to the database. – Adrian Larson Dec 5 '18 at 17:53
  • 1
    Why would you need to "addFields" if it's a new record? There's no purpose in doing so, since you can't query a record that doesn't exist. – sfdcfox Dec 5 '18 at 19:13
  • 1
    This is exactly why I was trying to figure out which exception you were running into. That's quite easy to work around. I thought you were running into an exception at a different step in the flow control. – Adrian Larson Dec 5 '18 at 19:27

You should just check if getId returns a value. Note that you can assign a String value to an Id variable.

Id recordId = controller.getId();
SObjectType sObjectType = (recordId == null) ?
    null : recordId.getSObjectType();

if (sObjectType == Account.SObjectType || sObjectType == Contact.SObjectType)
  • I guess I confused the usage of the method.The documentation doesn't mention anything about this being used to make a query, the exact line is "This method adds a reference to each field specified in fieldNames so that the controller can explicitly access those fields as well." Note the "access", not query. I assumed that the fields were used for more than just a basic query, and that a "reference" of some kind was needed for EVERY field regardless of context (none is given on that page, ie "dont call this for new records"). – battery.cord Dec 5 '18 at 19:46
  • Yep. It's a query. Sounds like an informative morning. :) – Adrian Larson Dec 5 '18 at 19:53

So, I guess the only way to get the type of an object in a ApexPages.StandardController without accessing the record itself is to use String.ValueOf to get a text representation of the object underneath. Thankfully, someone had the foresight to make this useful information.

String objectType = String.valueOf(controller);
// This string looks like this:
// "ApexPages.StandardController[Account]"
// So we can "parse" it and get that object name, 
// or we can use `Contains` to try and find a name inside the string 

if (objectType.contains('[Lead]')) {
    // Could also create new instance, or get describe by name 
} else if (objectType.contains('[Account]')) {
    // Doesn't have field, don't add anything 
} else if (objectType.contains('[Contact]')) {
    fields.add('Conditional'); // Standard field, as example 

This doesn't count as accessing the record and allows the controller to describe its type, even though it feels really brittle. Would really like another method to figure this out.

So this is actually a non-issue. AddFields is only nessescary when information has to be pulled out of the database, and does not affect the rendering of "new record" pages when not used. So, for any record with a valid ID, you can call the methods against the Id, for the rest of the records, you can just skip the call.

See: X-Y Problem.

  • So simple. What an interesting find. I'd probably use regex instead but very neat trick! if (Pattern.compile('ApexPages.StandardController[(Account|Contact)]').matcher(String.valueOf(controller)).find()) { fields.add('Condditional__c') } – Adrian Larson Dec 5 '18 at 18:47
  • Can't take credit for this one, I think I found the String.ValueOf trick somewhere else on the network. – battery.cord Dec 5 '18 at 19:20

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.