[SalesForce] Dynamically expanding a wrapper class for an object as custom fields are added.

I've reached a bit of a dead end in my latest programming endeavour. To give a brief overview I'm creating a console that houses data for four custom objects linked together loosely only by their intended purpose whilst still remaining independent.

The console simply outputs a custom list for each objects associated records, which can then be filtered by a number of variables, such as the project that owns the records, whether the records are active or not etc. Inconsequential information.

My problem comes when considering the scenario where a user may want to add a custom field to one of the custom objects (which they should be able to do). I've built the Visualforce to output the wrapper object fields statically and so my build does not allow for the dynamic adoption of any user-changes. Currently the wrapper class processes all possible fields from each object and for each field type does the necessary validation to assess whether the records field is blank or not etc. The Visualforce then assesses whether the wrapper field is null before outputting as standard. The user can select from a multi-select pick list which fields they'd like to see in the columns.

Is there anything I can do or can I take a different approach to allow for dynamic expansion of the wrapper class? I'd love their to be a simple way to perhaps assess the schema of an object and create any wrapper fields necessary based on the schema. Perhaps with pre defined rules for how to assess each field according to its Type.

The four objects share similarities in structure but have enough fields different to justify their independence. Additionally they weren't made under the same object as we liked the idea of having separate related lists on the project page for each one amongst other benefits in having separate objects. However if this is the wrong way to structure my data please let me know.

Any discussion or solution related to my problem is welcomed!

Best Answer

Here are a couple of general points on the subject...

Visualforce (and Apex) support map syntax so you can present in a basic way additional values established at runtime (e.g. obtained from describe calls or field sets or custom settings):

public class Wrapper {
    ...
    public String[] keys {get; set;} 
    public Map<String, String> extras {get; set;}
    ...
    public String[] contactFields {get; set;} 
    public Contact contact {get; set;}
    ...
}

<apex:repeat var"key" value="{!keys}">
    <apex:inputText value="{!wrapper.extras[key]}"/>
</apex:repeat>

But to get the full benefit of the metadata behind an SObject field (such as data type including parsing/formatting, picklist values, label, help text) it is best to directly use the SObject fields (as SObject also supports map-like access):

<apex:repeat var"f" value="{!contactFields}">
    <apex:inputField value="{!contact[f]}"/>
</apex:repeat>

And in Apex code there are string-based get/put methods available for any SObject too. So while you can't dynamically extend the wrapper class, you may be able to add code to it driven by data (such as lists of field names) that accomplishes the processing you require.

PS

To illustrate how code can work with a varying number of fields (including fields added by a user) this code will look at every field on a Contact object instance c:

public void doSomething(Contact c) {

    for (String f : Contact.SObjectType.getDescribe().fields.getMap().keySet()) {
        Object o = c.get(f);
        if (o instanceof String) ...
        else if ...
    }
}

and there is also API to get details of a field in a DescribeFieldResult.

Related Topic