1

I want to display Fields, e.g. Name, OwnerId, CreatedById, CreatedDate, LastModifiedId, LastModifiedDate of an sObject on Visualforce. The sObject is selected on the Visualforce page and then passed to the controller for further usage.

My wrapper Method looks like this:

public wrapperRecord getRecordById_additionalInfo(String selectedRecordId, String selectedObjectId) {
    if(selectedRecordId == null || selectedObjectId == null) {
      return null;
    }else{

        Schema.DescribeSObjectResult resultd = Schema.getGlobalDescribe().get(selectedObjectId).getDescribe();
        String objectFields = String.join(new List<String>(resultd.fields.getMap().keySet()), ', ');

        String soql = 'SELECT ';
        if (objectFields.containsIgnoreCase('Name')) {
            soql += 'Name, ';
        }
        if (objectFields.containsIgnoreCase('OwnerId')) {
            soql += 'OwnerId, Owner.Name, ';
        }
        if (objectFields.containsIgnoreCase('CreatedById')) {
            soql += 'CreatedById, CreatedBy.Name, ';
        }
        if (objectFields.containsIgnoreCase('CreatedDate')) {
            soql += 'CreatedDate, ';
        }
        if (objectFields.containsIgnoreCase('LastModifiedById')) {
            soql += 'LastModifiedById, LastModifiedBy.Name, ';
        }
        //if (objectFields.containsIgnoreCase('LastModifiedDate')) {
            soql += 'LastModifiedDate ';
        //}
        soql += ' FROM '+selectedObjectId;
        soql += ' WHERE Id=:selectedRecordId';

        list<sObject> lstUserRecords_additionalInfo = Database.query(soql);
        sObject UserRecords_additionalInfo = lstUserRecords_additionalInfo[0];

        list<wrapperRecord> wraplstUserRecords_additionalInfo = new list<wrapperRecord>();
        wrapperRecord wrapUserRecord_additionalInfo = new wrapperRecord(UserRecords_additionalInfo, objectFields);

        wraplstUserRecords_additionalInfo.add(wrapUserRecord_additionalInfo);

        if(wraplstUserRecords_additionalInfo.size() == 1) {
            return wraplstUserRecords_additionalInfo[0];
        }
        return null;
    }    
}

With the Wrapper defined as:

public class sObjectWrapper {


    public string RecordId{get;set;}
    public Id UserId{get;set;}
    public string HasDeleteAccess{get;set;}
    public string HasEditAccess{get;set;}
    public string HasReadAccess{get;set;}
    public string HasTransferAccess{get;set;}
    public string MaxAccessLevel{get;set;}
}

private class wrapperRecord {
    public String id { get; set; }
    public String name { get; set; }
    //public String ownerid { get; set; }
    public String ownername { get; set; }
    public String createdById { get; set; }
    public String createdBy { get; set; }
    public String createdDate { get; set; }
    public String lastModifiedById { get; set; }
    public String lastModifiedBy { get; set; }
    public String lastModifiedDate { get; set; }

    public wrapperRecord(sobject UserRecords_additionalInfo, String objectFields) {

        this.id = String.valueOf(UserRecords_additionalInfo.get('Id'));

        if (objectFields.containsIgnoreCase('Name')){
            this.name =String.valueOf(UserRecords_additionalInfo.get('Name'));
        } else{
            this.name ='n/a';
        }
        if (objectFields.containsIgnoreCase('OwnerId')){
            this.ownername =String.valueOf(UserRecords_additionalInfo.getSObject('Owner').get('Name'));
        } else{
            this.ownername ='n/a';
        }
        if (objectFields.containsIgnoreCase('CreatedById')){
            this.createdById =String.valueOf(UserRecords_additionalInfo.getSObject('CreatedBy').get('Name'));
        } else{
            this.createdById ='n/a';
        }
        if (objectFields.containsIgnoreCase('CreatedDate')){
            this.createdDate =String.valueOf(UserRecords_additionalInfo.get('CreatedDate'));
        } else{
            this.createdDate ='n/a';
        }
        if (objectFields.containsIgnoreCase('LastModifiedById')){
            this.lastModifiedById =String.valueOf(UserRecords_additionalInfo.getSObject('LastModifiedBy').get('Name'));
        } else{
            this.lastModifiedById ='n/a';
        }
        if (objectFields.containsIgnoreCase('LastModifiedDate')){
            this.lastModifiedDate =String.valueOf(UserRecords_additionalInfo.get('LastModifiedDate'));
        } else{
            this.lastModifiedDate ='n/a';
        }
    }
}

And the Page looks something like this:

            <apex:pageBlock title="Record Information: {!objRecordInfo.Id}" rendered="{!selectedObjectId!=null && selectedRecordId!=null}">
                <apex:pageBlockSection columns="2" collapsible="false" id="recordInformation”>

                    <apex:pageBlockSectionItem >
                            <apex:outputText value="{!objRecordInfo.name}" id="record__RecordName"/> 
                    </apex:pageBlockSectionItem>

                    <apex:pageBlockSectionItem >
                            <apex:outputText value="{!objRecordInfo.ownerid}” id="record__RecordOwner"/>
                    </apex:pageBlockSectionItem>

                    <apex:pageBlockSectionItem >
                            <apex:outputText value="{!objRecordInfo.createdById}" id="record__CreatedBy"/> 
                            <apex:outputText value=", {!objRecordInfo.createdDate}" id="record__createdDate"/> 
                    </apex:pageBlockSectionItem>

                    <apex:pageBlockSectionItem >
                            <apex:outputText value="{!objRecordInfo.lastModifiedById}" id="record__LastModifiedBy"/> 
                            <apex:outputText value=", {!objRecordInfo.lastModifiedDate}" id="record__lastModifiedDate"/>                       
                    </apex:pageBlockSectionItem>

                </apex:pageBlockSection>
            </apex:pageBlock>

The problem here is now, that the page works for all the objects which contain all the fields. For sObjects which do not contain the fields I do get an error:

System.QueryException: No such column 'Name' on entity 'Case'. If you are attempting to use a custom field, be sure to append the '__c' after the custom field name. Please reference your WSDL or the describe call for the appropriate names.

It seems the if clauses are ignored and all fields of the soql query outputted for each sObject. The wrapper class works fine when I put default values in. Does anybody have some advice here?

1

You don't need to do all this work to make your field checks case-insensitive; describe maps are by default case-insensitive. So you can just check if fields are in the keySet.

One more note, you can get the SObjectType from an Id. No need for a second parameter.

public static SObject query(Id recordId)
{
    SObjectType requestedType = recordId.getSObjectType();
    // no need to get the global describe!

    Map<String, SObjectField> fields = describe.fields.getMap();
    // this magical map has case-insensitive keys

    List<String> fieldsToQuery = new List<String>();
    // using String.join is much cleaner than list concatenation imo

    if (fields.containsKey('name'))
    {
        fieldsToQuery.add('Name');
    }
    if (fields.containsKey('OWNERID'))
    {
        fieldsToQuery.add('OwnerId');
        fieldsToQuery.add('Owner.Name');
    }
    // etc.

    return Database.query(String.format(
        'SELECT {0} FROM {1} WHERE Id = :recordId', new List<String> {
            String.join(fields, ','), String.valueOf(requestedType)
        }
    ));
}

Now with this generically re-usable utility function in hand, you can simplify your existing code quite a bit. One more change I would make, though, is using the getPopulatedFieldsAsMap method in your wrapper class.

public wrapperRecord(SObject record)
{
    this.id = record.Id;
    // dynamic get is overly verbose here

    Map<String, Object> data = record.getPopulatedFieldsAsMap();
    // this map is case-sensitive

    name = (String)data.get('Name');
    if (name == null) name = 'N/A';

    User owner = (User)data.get('Owner');
    ownerName = (owner != null) owner.Name : 'N/A';

    // etc.
}

Now you can have a very light controller method. Fat service, skinny controller. That's the way to go.

public wrapperRecord getRecordById_additionalInfo(String selectedRecordId, String selectedObjectId)
{
    // you don't really need to pass in the object type any more

    if(selectedRecordId == null) return null;
    // you don't need an else block when using a guard clause

    SObject record = query(selectedRecordId);
    return new WrapperRecord(record);
}

There's so little logic left you can actually one-line it:

public wrapperRecord getRecordById_additionalInfo(String selectedRecordId)
{
    return (selectedRecordId == null) null : new WrapperRecord(query(selectedRecordId));
}
| improve this answer | |
  • 1
    Apologies for the late reply and thank you very much, the code restructuring suggestions indeed were helpful. – user40501 Mar 7 '17 at 14:52
2

Your code will add name to the soql statement if any field on the object contains the string "name." Case has a field called "CreatorName" and another called "SuppliedName," which will both pass the if statement. In your case, I'd recommend adding a method like this:

public static boolean setContainsIgnoreCase(Set<String> toSearch, String query)
{
    for (String i in toSearch)
    {
        if (i.equalsIgnoreCase(query))
        {
            return true;
        }
    }
    return false;
}

And use this instead of joining your set into a string.

Since the field names aren't actually being put in by the user, you could also just use contains to check for them with lowercase names, like they are listed in the describe.

| improve this answer | |
0

Converting the field names into a single string risks matching a field called e.g. "Name" with some other field such as "OtherName__c". So one piece of advice is to keep the fields separate:

// Field names are returned in lower case
Set<String> fields = resultd.fields.getMap().keySet();
if (allFields.contains('Name'.toLowerCase())) {
    ...
}

You will also need to conditionally render the fields in the Visualforce.

| improve this answer | |
0

I would prefer this approach. Get the field map and compare with API Names.

As it will be better to compare directly with API names. In your code you are using contains 'Name' which is giving the error.

Schema.DescribeSObjectResult resultd = Schema.getGlobalDescribe().get(selectedObjectId).getDescribe();


String soql = 'SELECT ';

Map<string, schema.sobjectfield> fieldMap = resultd.fields.getMap();
if (fieldMap != null)
{
    // Loop through all fields.
    for (Schema.SObjectField f : fieldMap.values())
    {
        // Describe each field.
        Schema.DescribeFieldResult fd = f.getDescribe();

        // This is the API name.
        if(fd.getName().equals('Name'))
        {
            soql += 'Name, ';
        }
        else if(fd.getName().equals('OwnerId'))
        {
            soql += 'OwnerId, Owner.Name, ';
        }
        else if(fd.getName().equals('CreatedById'))
        {
            soql += 'CreatedById, CreatedBy.Name, ';
        }
        else if(fd.getName().equals('CreatedDate'))
        {
            soql += 'CreatedDate, ';
        }
        else if(fd.getName().equals('LastModifiedById'))
        {
            soql += 'LastModifiedById, LastModifiedBy.Name, ';
        }       
    }
    soql += 'LastModifiedDate ';
    soql += ' FROM '+selectedObjectId;
    soql += ' WHERE Id=:selectedRecordId';
}
| improve this answer | |

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.