Working with the Drupal Computed Field Module
When setting up content types on a more involved site, a developer could easily end up with having an enormous list of fields in one single content type. When this happens, I tend to pause for a second, and think through whether there is any way to consolidate that list. Chances are, I find a field or two whose values either depend on other fields or can be generated without user input. These are the exact scenarios where the Computed Field module can be a great choice.
Computed Fields lets you define an algorithm to calculate the value of a certain field, freeing the end user from having to unnecessarily fill in data that can be derived in an automatic fashion. The module has Drupal 6 and Drupal 7 versions. Although as of the time of writing the D7 version is still in beta, I myself have not encountered any problems while using it.
The module provides two methods to store the code that calculates the field value: you can enter it in the field configuration form, or you can store it as a function in a module. I personally prefer the latter one, because it is easier to manage. If the code is in a module, you can keep track of it using version control systems, and it also makes the process of deploying code from development to production less error prone.
As an example, let’s suppose I have a content type that represents products, and I have a ‘price’ and a ‘cost’ field in it, and now I wish to add a ‘profit’ field. Since profit would be the difference between the price and the cost, it would not make sense to force the user to enter this data separately. I’ll go ahead and create a computed field for profit. When adding the field, I am presented with the field settings form that includes a textbox for the code that calculates the field value. This textbox is titled ‘Computed Code (PHP)’, as the figure below illustrates.

As the help text explains, the computed value has to be assigned to the $entity_field[0]['value'] variable. The node object is accessible through the $entity variable in the code snippet. Through that variable, I can access the values of any other fields of the node. My code snippet that calculates the profit looks like as follows, assuming the ‘price’ field is called field_price, and the ‘cost’ field is called ‘field_cost’.
$entity_field[0]['value'] = $entity->field_price[LANGUAGE_NONE][0]['value'] - $entity->field_cost[LANGUAGE_NONE][0]['value'];
If I wanted to store this code in a module, I would need to create a function with the name computed_field_field_profit_compute. The exact argument list is provided on the field edit form for reference.
function computed_field_field_profit_compute(&$entity_field, $entity_type, $entity,
$field, $instance, $langcode, $items) {
$entity_field[0]['value']= $entity->field_price[LANGUAGE_NONE][0]['value'] - $entity->field_cost[LANGUAGE_NONE][0]['value'];
}The function name pattern that needs to be used is computed_field_{field_name}_compute. Unfortunately, though, the module does not provide a hook system, where other modules would be able to override my implementation of the calculation. In fact, multiple modules cannot even implement the calculation for the same field, because that would cause a collision in function names.
The field value is recomputed when the node is saved. By default, the value is stored in the database, and does not get recomputed every time the node is viewed or loaded. This could have serious implications if the computing code is not written wisely. As a rule of thumb, no variables that are outside the node should be referenced in the computed field, because those variables can change without the computed field ‘knowing’ about it. Doing so might cause discrepancy between the value that is stored in the database and the actual value that the code yields. For instance, let’s suppose I set up the price field as a computed field, and use the $user object in the code to show different prices to different users. Because the field is updated only when the node is saved, it will be calculated when an admin, say User A, saves the node, and the price that User A is supposed to see will be saved to the database. When a regular user, say User B, visits the site, the value he sees will be the value that is in the database, which is the price that was calculated when User A was logged in. User B will never see the price that he is supposed to see, unless he edits the node.
To prevent this problem, one might opt to not save the computed value to the database. This can be done by unchecking the ‘Store value in the database’ option on the field edit form. This way of handling things has its own shortcomings as well. The most obvious one is that the value will be recalculated very often, and, depending on the complexity of the code, it might have a negative effect on performance. Another remarkable downside of this method is that the field cannot be used in Views. You cannot display a calculated field; nor can you use it as a filter in a view, if the data is not stored in the database.
The computed field edit form also has a textbox, titled ‘Display Code (PHP)’, where the developer can specify a piece of code that will be executed every time the field is displayed. Using this feature, you can manipulate the value before it is displayed. An example use of this would be to display a $ sign in front of a dollar value. This code can also be supplied by a module; the function you will need to implement is computed_field_{field_name}_display().
5 comments
Very nice tutorials :)
Very nice tutorials :)
I've learnt a lesson
"As a rule of thumb, no variables that are outside the node should be referenced in the computed field, because those variables can change without the computed field ‘knowing’ about it. "
I just learnt a lesson about this. However, I want to find a way to get around it. Do you have a way?
Store value in the database
You can try unchecking the 'Store value in the database' option. That will force the field to be recalculated every time, and so it does not get outdated when your variables change.
But I need it in a view as a
But I need it in a view as a sortable column. Based on your article, I can't.
Force update
In that case, the only option I see is to force updating the computed field every time your variable changes (the one that is not coming from the node).
Post new comment