Org-only meta-data

Many years ago I used a development tool that kept a local cache of the project. Common enough still today, but in that case it was in an opaque binary form and so when it got corrupted there wasn’t much choice but to throw away all your recent work and start again with a fresh copy from the central shared repository. You both lost the work that you had done and wasted time getting the the local cache consistent with the shared repository.

I had the same experience this morning with Force.com. The “opaque binary form” in this case was the meta-data that resides solely in the Force.com org. I deleted some (heavily referenced) classes and triggers by accident and then could not re-create them getting the error message “… name is already in use or has been previously used”. Dead in the water. Several hours of work were needed to create a new org, modify package prefixes, deploy, manually re-create the packaging and do some basic testing.

So bear in mind that while version control such as SVN provides some protection, the meta-data that resides in an org is a hidden part your project that you just have to hope will never get corrupted (or need branching or reverting). And the level of voting for these ideas sf:clean and sf:package suggests this is not going to change.

A SOQL relationship query example

I have a few cases where a custom object can be related to either a Contact or an Account. My first thought of how to access the Contact or Account information in business logic Apex was to use two SOQL queries like this (where PolicyAssociation__c is just a junction table):

List<Contact> contacts = [
        select Name, MailingPostalCode
        from Contact
        where Id in (select Contact__c from PolicyAssociation__c where Policy__c = :policyId)
        ];
List<Account> accounts = [
        select Name, BillingPostalCode
        from Account
        where Id in (select Account__c from PolicyAssociation__c where Policy__c = :policyId)
        ];

But in the governor limited world of Force.com, using two queries everywhere to access one set of objects (either Contact or Account) seemed a little reckless.

Unlike SQL via JDBC, SOQL allows data from multiple objects to be conveniently obtained in one query via the relationship “fields”. It is well worth reading and re-reading A Deeper look at SOQL and Relationship Queries on Force.com to understand what is possible. When you first build a data model you will probably accept the default names for these relationships but it is worth reviewing the naming – particularly the master to detail name – to ensure that they are consistent and clear when used in SOQL.

So I am now using this pattern where a single query gets the Contact and Account objects albeit at the cost of a tedious to type loop over the PolicyAssociation__c objects:

List<PolicyAssociation__c> pas = [
        select Contact__r.Name, Contact__r.MailingPostalCode, Account__r.Name, Account__r.BillingPostalCode
        from PolicyAssociation__c
        where Policy__c = :policyId
        ];
List<Contact> contacts = new List<Contact>();
List<Account> accounts = new List<Account>();
for (PolicyAssociation__c pa : pas) {
    if (pa.Contact__r != null) {
        contacts.add(pr.Contact__r);
    }
    if (pa.Account__r != null) {
        accounts.add(pr.Account__r);
    }
}

Displaying links to peer objects in Visualforce

I have a custom page that displays one of a collection of child objects of a parent object and wanted to add links that allow the page to switch to any of the other child objects. The code below shows one way to do this, presenting the links as a comma separated list (in a sentence) and showing plain text for the current child object as there is no point in linking to the already displayed page.

The controller (see bottom of this post) has two properties: child which is the current object and children which is the collection of all the child objects with just the Id and Name properties queried.

Here is the Visualforce:

<apex:panelGroup layout="inlined" rendered="{!children.size > 1}">
    <apex:variable var="first" value="{!(true)}"/>
    <apex:outputText>Other child pages </apex:outputText>
    <apex:repeat var="item" value="{!children}">
        <!-- Separator -->
        <apex:outputText rendered="{!!first}">, </apex:outputText>
        <apex:variable var="first" value="{!(false)}"/>
        <!-- Current object's link not clickable -->
        <apex:outputText value="{!item.Name}" rendered="{!item.Id == child.Id}"/>
        <!-- Other object links clickable -->
        <apex:outputLink value="/apex/ChildPage?id={!item.Id}" rendered="{!item.Id != child.Id}">{!item.Name}</apex:outputLink>
    </apex:repeat>
    <apex:outputText>.</apex:outputText>
</apex:panelGroup>

A couple of points to note:

  • The use of an apex:variable to introduce variables into loops such as apex:repeat or apex:pageBlockTable. This code is using a boolean flag; note that without the round brackets true or false are interpreted as controller properties. But a variable can also be used as a loop counter allowing say an item number to be output.
  • The use of an inlined apex:panelGroup – a HTML span – to control the rendering of a set of nested components avoiding having to specify the rendered attribute multiple times on each component.

And here are the properties the controller needs:

// Set from e.g. getRecord method of standard controller
public Child__c[] child {
    get;
    private set;
}
// Peer objects
public Child__c[] children {
    get {
        if (children == null) {
            children = [
                    select Id, Name
                    from Child__c
                    where Parent__c = :child.Parent__c
                    order by Id
                    ];
        }
        return children;
    }
    private set;
}

Filtering picklists by RecordType in Visualforce

When you add a record type to an object, you can sub-set the picklist entries per record type. The default UI respects this configuration but Visualforce will not until this idea visualforce please respect profile/record type for picklists is delivered; it is presently marked “Coming in the Next Release”; it is now marked “Delivered in Winter ’11” so the work-around below should no longer be needed.

Here is an example of the sort of work-around that you have to resort to at present as a stop-gap.

First, in the Visualforce itself, instead of:

<apex:inputField value="{!adjustment.Type__c}" required="true"/>

you need this (made messier by the panels that achieve the “required” styling):

<apex:pageBlockSectionItem >
    <apex:outputLabel value="{!$ObjectType.Adjustment__c.fields.Type__c.label}"/>
    <apex:outputPanel layout="block" styleClass="requiredInput">
        <apex:outputPanel layout="block" styleClass="requiredBlock"/>
        <apex:selectList value="{!adjustment.Type__c}" size="1">
            <apex:selectOptions value="{!typeOptions}"/>
        </apex:selectList>
    </apex:outputPanel>
</apex:pageBlockSectionItem>

Then controller code needs to be added to build the option values. The real killer here is that there is presently no API to obtain the picklist entries for each record type. That means that the mapping must be duplicated in code (the accept method in the code below). Perhaps this is tolerable for a picklist or two with simple mapping, but crazy where many picklists, picklist entries, record types and different mappings are involved.

So for a simple mapping case, instead of no code in the Apex controller, code and data like this has to be added:

public List<SelectOption> typeOptions {
    get {
        if (typeOptions == null) {
            typeOptions = new List<SelectOption>();
            typeOptions.add(new SelectOption('', '--None--'));
            DescribeFieldResult d = Adjustment__c.Type__c.getDescribe();
            for(PicklistEntry e : d.getPicklistValues()) {
                if (e.isActive() && accept(e)) {
                    typeOptions.add(new SelectOption(e.getValue(), e.getLabel()));
                }
            }
        }
        return typeOptions;
    }
    set;
}
private Boolean accept(PicklistEntry e) {
    if (adjustment.RecordTypeId != AdjustmentRecordType.getTermLife()) {
        // Only show the entries not in the hide set
        Set<String> hide = new Set<String>{
            'State Income Tax',
            'Life Interest (Pre-Tax)',
            'Loan (Pre-Tax)',
            'Dividends (Pre-Tax)'
            };
        return !hide.contains(e.getValue());
    } else {
        // Show all entries
        return true;
    }
}
public PageReference save() {
    // Work-around for ugly id being displayed to user if required="true" is used on the apex:selectList.
    if (adjustment.Type__c == null) {
        adjustment.Type__c.addError('You must enter a value');
        return null;
    }
    return standardController.save();
}

See this post for some background on doing the required check in code in the save method.

If you find that you are having to do a lot of this sort of coding, you might be able to build on Sam Arjmandi’s A Picklist Component For Your Visualforce Pages by finding a way to hook in the record type to picklist entry mapping. Also remember that there is presently a governor limit of 10 on field describes.