Why it is *Always* Better to Use Integer Select Lists in Drupal


When I say "always" I really mean more often than not, because nothing is black and white and there's always an edge case that puts you in a grey area. However for the sake of this discussion, we "always" user integer select lists. Here's why we do it this way:

Options within a select list have two distinct pieces – the "machine_value" and "human_value". The machine value is what is stored in the database and the human value is what is displayed to a user.

If we were to look at the options of a simple select list they might appear like this: (machine_value|human_value)

  foo|foo
  bar|bar
  var|var

Seems pretty straightforward, right? The user would see the human readable options foo, bar, and var. Then once stored in the database they would be stored as foo, bar, or var.

But what if two months down the road the requirements change? What if "var" is no longer an option and it needs to now be "newvar"? Unfortunately you're SOL if you want to change the options in the select field, as the machine_name cannot be modified within Drupal. You could go run some MySQL queries and change the database schema manually (bleh!). You could add the new machine_value|human_value and manually go through editing all the existing entities to point to the new option(bleh!!). Or you could simply leave the machine_value the same and modify the human_value so it looks like this (bleh!!!):

  foo|foo
  bar|bar
  var|newvar

None of these options are very optimal or efficient and there a bunch of other issues that can arise from implementing them. Here at Chronos Interactive our standard of practice is to utilize the integer type select list, which has us creating our (machine_value|human_value) options like so:

  0|foo
  1|bar
  2|var

By taking this approach we isolate the machine_name value so it is ultimately inconsequential. Creating a human readable value that isn't specifically tied to the machine value means the two can continually change with little issue. Code behind can function based off the integer value, and all user interfaces get the human readable value.

Which means we can go in and change our options to:

  0|foo
  1|bar
  2|newvar

or even:

  0|newvar
  1|bar
  2|foo

The changes instantly take effect in every instance of the entity because of the abstraction layer in Drupal's field schema.

With this practice there are times where we need to show the human_value, whether this is in the theme layer, through an API, or some other scenario. The "problem" is that when an entity is loaded, the value of the field for the entity object is the machine_value. For example, you wanted to build an array of some entity values and didn't want to have the field's value 0, 1, or 2 shown but rather the values newvar, bar, or foo.

Here's a simple snippet of code which provides the solution. A custom function which allows you to pass that integer value to it and return the human_value.

/**
* Custom function to return human_value string from machine_value integer key of a select list
* $int_key is the integer key value
* $field_name is the field name of the select field
*/
function _YOUR_MODULE_NAME_select_list_intkey_to_humanstring($int_key, $field_name) {
  // load field info
  $field_info = field_info_field($field_name);
  // load allowed values
  $field_info = list_allowed_values($field_info);

 

  // return key to value
  return $field_info[$int_key];
}