Wednesday 9 February 2011

Describe, and Schema, and SObject and Oh my! Part 2

In my previous post, I talked about using Schema, SObject and Describe objects to help make code generic and usable across different types of objects. To recap: when creating/updating/deleting an object, a corresponding object should be created/updated/deleted with a small sub-set of details copied across to the new record. The previous post looked at how to automatically determine the type of object should be created (or edited or deleted – I'm sure you get the idea by now) when a record is inserted.

So we have a way of working out what type of object we need to create. How do we go about copying values between the different types of object? As different objects have different fields, we need a way of knowing which fields to copy for which objects. Once again we turn to our good friend Map:

Map<String, Map<String, String> > fieldsByObjectName =

new Map<String, Map<String, String>> {'Account' => new Map {'Name' => 'Name','OwnerID' => 'Custom_Field1__c', 'ID' => 'Account__c'}};

What an eyesore – but not as difficult as it may first look. We have a map (let's call it the outer map) where the values themselves are a map (inner maps). The inner maps hold the details of what fields should be copied for an object; we can get these values from the outer map by get()ing the values associated with the object name of the object being inserted.

Let's look how that applies to the example above. fieldsByObjectName.get('Account') returns a map of strings, keyed by strings. These hold the fields that should be copied from the account to the custom object, and what the corresponding field is on the custom object.

Usefully, the generic SObject has put() and get() methods to retrieve and set field values. Get() takes a field on an SObject for which the value should be returned; put() takes a field and a value and sets the field to the value given. As we have these values in a collection, we can simply loop over the collection and copy the fields with these methods:

Map<String, String> fieldsToCopy = fieldsByObjectName.get(objectName);

for(String field : fieldsToCopy.keySet()) {

newObject.put(fieldsToCopy.get(field), ourNewObject.get(field));

}

Some relatively straightforward DML plumbing at the end of the code handles inserts/updates of the necessary objects.

So, the end result is code that will extend quite nicely if I need to do this for other objects. I just need to create a new custom object with fields to hold values; update the code to map the object to the new custom object, along with what fields should be copied; and finally add a simple trigger that calls the relevant method depending on the type of operation performed.

So are there any downsides?

Though the code is extendable, I don’t think it’s as easy to read as doing it the long-handed way. There’s a whole discussion to be had about the pros and cons of this, however, I think that it would take someone looking at the code longer to understand this approach than simply having triggers and code for each object.

My solution doesn’t do anything to convert between different field types – not really an issue for what I was doing, but if you had a source field of one type and a destination field of another type, some sort of conversion may need to take place. At this time, I'm not sure how I'd approach that.

Where updates occur to objects, I had to hard code queries to retrieve the objects with the sub-set of details so that I could update them (or delete them in the deletion case). This is because I had a set of IDs to use in the query, and had I constructed a query string, there was no easy way to include them in the string. This did feel as if I was breaking the principle that led me down the route in the first place. However, it was “just” a proof of concept and, more importantly, a learning opportunity for me and all things considered, I’m pretty happy with the end result.

No comments:

Post a Comment