Mastering Custom Settings

“Let the games begin”

Advertisements

CustomSettingsStart

Custom Settings feature has remained unchanged for a long time now. You’d say ‘why change something that works so good?’. Absolutely! But are you using them correctly? 

To me it’s been a long time since last time I visited the Custom Settings documentation. So I dived into it and the truth is that, it took me a couple of reads of the Custom Setting methods docs to get all the details.

At the end Custom Settings are quite easy to use once you play around a bit with them, but have some secrets you must know! I got fun finding them and I hope you do too!

Intro – The Theory

What is a Custom Setting?

Custom Settings are custom objects aimed to store your application’s configuration. You’ll probably need to access that information often to make your app to behave accordingly. Salesforce knows that and that’s why the custom setting records are cached in memory and ready to be consumed without any effort on your side.

To benefit from the cache, you have to use specific methods we’ll see soon. However, as they are SObjects (a special kind of them) you can also run a SOQL query to get their records. Just take into account that that will sum up one SOQL request to your so appreciated governor limits budget. This can be your preferred method in certain number of cases in which you are striving for a more flexible code at run time though. It is up to you!

What are the different custom settings for?

I know. I can’t contain your adventurous spirit, so you’ve probably already discovered that there are two kind of custom settings. Thanks to those, you will be able to expand your settings to multiple purposes.

The two different types are: “Hierarchy” and “List”. You can read the full definition here, but in summary, they both share the aforementioned aim of storing (generally static) configuration information.

Such config data can be a specific user personalisation settings. Hierarchical settings are which work better for that goal. Or it can be lists of generic values to define application-level data. You guessed it, you can use Custom Settings of List type for this*.

Hierarchy type Custom Settings, are the most interesting ones and we’ll get more fun with them in a minute. But if you are new using them, now you might be wondering why they get such name. It is because the information they store is organised into three levels (hierarchically – obviously!): Org wide, profile and user level. Users can request a value and get what matches first, in the following order:

  1. User Id
  2. Profile Id
  3. Org wide
  4. null

So for instance, when a hierarchical custom setting value is set at org wide level, all the users will inherit such value unless there is one defined at user level for them. Is that simple! There are also some advanced details we’ll see in a jiffy, but you’ll get them better with some examples.

* Although Salesforce still allows you to use List Custom Settings, they recommend you to use custom metadata types instead. You’ll even notice that the Setting Type is read only and set to Hierarchy by default. You can enable them though. So we’ll see how to use them too.

custom settings list type caveat

Level 1 – The Countless Game

CountlessGameIcon Let’s say we are developing the Countless game. The player must match pairs of numbers in a board in a sequential order before a given time expires. Each time the board is cleaned up, a new one is populated with new numbers. Be careful, once you try it, you get addicted. Just sayin’.

One aspect we could make available for configuration, is the device orientation, like portrait or landscape. We can create a custom setting for that.

But what kind of game doesn’t let you exchange your points (coins) into fancy and full of color new interfaces! You can do that in the Countless game, but you would probably expect your settings to not affect other users configurations, don’t you?

This is a job for… Hierarchy Custom Settings! (HCS for her fans). So let’s put this into practice. Now open your dev environment and go to “Setup > Custom Code > Custom Settings”.

CustomSettinsSetupHome

Click on “New”, fill the mandatory fields and save it. To follow this article better, use the following information:

Label: Look and Feel Settings
Object Name: LookAndFeelSettings
Setting Type: Hierarchy
Visibility: Protected

NewLookAndFeelSetting.png

Now we need a couple of fields. Not all the field types are available for Custom Settings. Picklist fields aren’t, for instance. In my case, I decided to use a text field and a number, but you can use any other type you think suits our needs.

I defined the two fields like follows:

Field Type: Text
Field Label: Orientation 
Length: 80
Field Name: Orientation

Field Type: Number
Field Label: Color Scheme 
Length: 2
Decimal Places: 0
Field Name: ColorScheme

LookAndFeelSettingDetails.png

Ok. We are done with the set up. To create records, click on the “Manage” button, fill the details and save.

You’re avid eyes may already have noticed that there are two buttons to do that. One is at the “Default Organization Level Value” section and another at the child section underneath. Those are quite descriptive by their own, so feel free to play around and create as many records as you want to get familiar with the interface. Just make sure you delete all of them before continuing with the next section.

Level 2 – Hands On Area

Warning: Don’t do this at home. No, wait. Definitely do this at home!

In games, first levels are usually focused on making you to get familiar with the controls. But now things become harder! We are going to write some code, so open your console now (not literally, just the developer console).

How can I add custom settings values with apex?

Do you remember we said Custom Settings are SObjects? The regular CRUD methods also work for Custom Settings. Try the following code into your console and check what happens.

LookAndFeelSettings__c customSetting
    = new LookAndFeelSettings__c();
customSetting.ColorScheme__c = 0;

insert customSetting;

If you have checked the custom setting values in your org, you should have seen something like this:

defaultValueDefined

That’s a org wide setting, which means that it is intended to apply to all users. So how can we create a user specific or profile value? This is done by setting the user or profile ID into the SetupOwnerId field. You can get such IDs by using the methods getUserId() and getProfileId() from the UserInfo class. Now run the following code and check the results:

LookAndFeelSettings__c userLevelSetting
    = new LookAndFeelSettings__c();
userLevelSetting.Orientation__c = 'Portrait';
userLevelSetting.SetupOwnerId = UserInfo.getUserId();
insert userLevelSetting;

If you refresh your Custom Settings page, now you should see the new child record. So the records we have are telling us that, by default, all users will get the Color Scheme ‘0’ config value, but our user has a specific configuration to get its very own device orientation. If you repeat the last code block, you’ll get an error, because there is already a value defined for that user. Easy peasy!

How can apex give me the values I insert?

Here is where the real fun comes in. Ideally, to get the most of custom settings, you will be using the method getInstance() or getValues(). These methods take the values from the cache and don’t consume any query at all. But before digging into their details, let’s do a test with the getInstance() method. Run the following code on your developer console and try to figure out why are you getting those results.

System.assert(false, ''
    + LookAndFeelSettings__c.getInstance());

Did you find anything interesting between all the values you have got? No? Have a second look.

CSUserMixedValues

Both, Orientation__c and ColorScheme__c fields have values, but these come from the two different setting records. Well, these are the details I said we would see later. And that time has arrived. So let’s get a bit more specific definitions of the methods we have available for Custom Settings.

The getInstance() method returns all the values found in any record of the Custom Setting hierarchy in the order: current user, profile, org wide.

The getInstance(userId) method returns all the values found in any record of the Custom Setting hierarchy in the order: specified userId, profile, org wide.

The getInstance(profileId) method returns all the values found in any record of the Custom Setting hierarchy in the order: specified profileId, org wide (yes, the user setting is not returned here).

The getInstance(orgId)… Ha! Just kidding. This method overload doesn’t exist. You can’t get the settings from a different org, can you? To get the org wide settings you must use the method getOrgDefaults() instead.

The most important thing you have to understand here is that, Hierarchy Custom Settings has been written to provide always the configuration that applies to the current user. This is because the values in the previous tests were becoming mixed from the user level and the org defaults. If there were a ColorScheme__c value at the profile level, it should be returned instead of the one from the org wide one. Let’s prove that. Insert a value at profile level by executing the following code:

LookAndFeelSettings__c profileLevelSetting
    = new LookAndFeelSettings__c();
profileLevelSetting.Orientation__c = 'Landscape';
profileLevelSetting.ColorScheme__c = 2;
profileLevelSetting.SetupOwnerId = UserInfo.getProfileId();
insert profileLevelSetting;

CSAllLevelValuesDefined

You should have something similar to the screenshot above. Now, run again the code that was printing the user values. Let me repeat it to avoid making you to scroll up again:

System.assert(false, ''
    + LookAndFeelSettings__c.getInstance());

CSAllValuesMixed.png

You should be getting the Orientation__c configuration you have set at your user level, but the ColorScheme__c coming from your profile. The org defaults are ignored. You get whatever is more specific to your user (what becomes first in the hierarchy). I encourage you to take some minutes of your time to experiment with the methods we have just learnt.

You should probably be wondering what if I don’t want the values to be overridden from any of the parent levels in the hierarchy. And I’m glad you ask about that, because you can get the values from a specific level by using the getValues() method. It takes the user or profile ID as param, and returns only the values at that level or null if it doesn’t exist. For instance run the following code:

LookAndFeelSettings__c currentUserSettings =
    LookAndFeelSettings__c.getValues(UserInfo.getUserId());

String orientation = currentUserSettings.Orientation__c;
Decimal colorScheme = currentUserSettings.ColorScheme__c;
String settingName = currentUserSettings.Name;

System.assert(false, settingName + '. ' +
    'Orientation = ' + orientation + '. ' +
    'Color Scheme = ' + colorScheme + '.');

The ColorScheme__c is now null because we wanted to ignore the values coming from any parent in the hierarchy.

ExclusiveUserValues

As you can see in the screenshot, the name of the custom setting record indicates it comes from the user level. But don’t trust on this all the time. If you have a look at the previous pictures you’ll see that we got the same name although some values where coming from different levels. It just indicates that there is a record at that level, not that all values come from it.

Bonus! – Deleting a Custom Setting with apex

With the values as they are right now, what do you think will happen if you run the following?

delete LookAndFeelSettings__c.getInstance();

Hummm… Ok. If you paid attention to the screenshots, there were an ID on them. And you are right, it will remove the setting record at user level, but will leave the others. However if you run it for a second time, you’ll get an error. Don’t be shy, try it again.

CSDeleteError

Let’s see what is happening. If you run our very famous statement again:

System.assert(false, ''
    + LookAndFeelSettings__c.getInstance());

You’ll observe the ID has gone (and the Name changed):

CSInheritFromProfile

You can still delete it though if you specify the profile ID as you learnt by using the method getInstance(profileId). So a valid thinking would be “Nah! I’ll always indicate the ID when deleting a custom setting”, but be careful with that. As you know, you can still get values from a parent if you use the getInstance(userId) method. Now that you’ve delete the user record, run the following:

System.assert(false, '' +
    LookAndFeelSettings__c.getInstance(UserInfo.getUserId()));

We get the same result than before. As we’ve just seen, if we trust on what the getInstance(userId) method returns to delete the user value, we’ll get an error. So instead, use the getValues(userId) method and check the value returned is not null.

Secret Level!

Know what? You said I could also get the values through SOQL. Wouldn’t that return exactly the values I want to get?

Yeah, good catch! Also, as you already may have noticed, the methods the Custom Settings provide to get their values, are a bit inflexible if we think on dynamic coding. But do you remember that stuff about “benefit from the cache … blah blah blah… governor limits budget… blah blah…” ? Don’t forget you’ll have to pay the bill not only for your extra query, but also for the time it takes to be read from the database. At the end, this means, your user experience.

If the number of times you expect to read your settings is insignificant, the SOQL approach can suit to you. I can feel your excitement, so let it flow by running the following query:

System.assert(false, '' +
    [SELECT Name, Orientation__c, ColorScheme__c
    from LookAndFeelSettings__c
    where SetupOwnerId = :UserInfo.getProfileId()]);

Voilà!

CSProfileValueFromSOQL

Wait! It seems we are close to the end. What about the Custom Settings of list type?”

Good. I said they are not as interesting as hierarchy type are. But a promise is a promise. This won’t be super intense though. Lists are lists. If you want to use List Custom Settings instead of Custom Metadata Types, you can bring the values of a specific row of such list by using the methods getInstace(dataSetName) and getValues(dataSetName) interchangeably. If you want the entire list of settings, you can get it by using the method getAll() and that’s all (pun for free:-)). This returns a map in the form Map<String, YourCustomSetting__c> where the key is the data set name. As always, a lot more fun here.

Final Stage – The “source” of truth

Ok, I know everything about Hierarchy Custom Settings. Now what?”

Well, probably, when you start coding your custom setting “queries”, you’ll want to have a class where to place them all, much like you’d do with your other SObjecs with the selector pattern. It is true that the cache-ish way to get the values isn’t super flexible. Moreover, Custom Settings are in the practice SObject like any other custom object you might have, but with additional methods. Given that the SObject class can’t be modified to implement any other interface nor extend from another class, it makes the problem a little bit harder.

So I had a look into my developer toolbox and unfortunately I discarded many options quite quickly. This is because, any attempt to generalise the code, is requiring the real instance of the Custom Setting. To get the value from the cache, you are forced to use the getInstance() method and that’s not something you can do dynamically. So instead of giving you the definitive solution, I’ll give you some advices:

  1. Don’t overcomplicate things.
    Working with Custom Settings is not a very big challenge. You’ve seen they are pretty simple. So, based on the above, don’t try to create abstract classes with super useful methods you will use rarely. If you try to generalise this, you’ll get soon a lot of casting sentences everywhere. Keep the problem small.
  2. Separate custom settings by process.
    Don’t try to have all the custom settings all together in the same class. Probably, you processes will have to consume only one of them at a time. It doesn’t worth it to end up having the typical “Utils” class. The more as you write there, the more heap size you’ll need.
  3. Avoid caching what is already in the cache. You are working with something that Salesforce has optimised for you. So think carefully on what you store in static vars. and save some Heap memory.
  4. Provide methods that are easy to understand for anybody to get your field values.

If we group all this up, we get something like the following. Hope this helps:

public class LookAndFeelSettings
{
    private static Decimal DEFAULT_COLOR_SCHEME = 0;
    private static String DEFAULT_ORIENTATION = 'Portrait';

    public static Decimal getCurrentUserColorScheme()
    {
        Decimal colorScheme =
            LookAndFeelSettings__c.getInstance().ColorScheme__c;

        if (colorScheme == null)
            return DEFAULT_COLOR_SCHEME;

        return colorScheme;
    }

    public static String getCurrentUserOrientation()
    {
        String orientation =
            LookAndFeelSettings__c.getInstance().Orientation__c;

        if (orientation == null)
            return DEFAULT_ORIENTATION;

        return orientation;
    }
}

If you still want to give it a go to generalisation, I recommend you try using dependency injection. I hope you get a lot of fun with your custom settings adventure!

Special Thanks

Countless is an original Valerio Sevilla’s game. Thanks a lot for letting me to make a reference to it on this post.

Thanks to Daniel Exposito for chasing me to write this blog post. It wouldn’t have been possible without your impatience…

Thanks for playing

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s