Working with the Drupal Computed Field Module

Working with the Drupal Computed Field Module

Gergely Lekli's picture

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().

Comments

Very nice tutorials :)

"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?

Gergely Lekli's picture

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 sortable column. Based on your article, I can't.

Gergely Lekli's picture

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).

Would you be able to show me how to display the total value on views on this code?

fields: total = value + value2

Computed Code: $entity_field[0]['value'] = $entity->field_value[LANGUAGE_NONE][0]['value'] - $entity->field_value_2[LANGUAGE_NONE][0]['value'];

Display Code (PHP) : $display_output = $entity_field_item['value'];

i tried to use this on views: Content: Total (Total) , but it doesnt display any total, any thoughts on this?

Thanks for the help.

Gergely Lekli's picture

Does it not display anything at all in views? If so, you could check if you have enabled the 'Store value in the database' option in the field settings.

Very nice tutorial..

Hello !
I need to get a field with URL alias of a node in Views in Drupal 7.
I put the following code in the Computed Code textfield:

$entity_field[0]['value'] = drupal_get_path_alias('node/' . $entity->nid);

but that does not seem to work in Views. Nothing is displayed at all, even if the storing to db is checked.
Thanks for reply

Gergely Lekli's picture

Hi,

This seems to be a valid use of a computed field. However, note that if the computed value is configured to be saved in the db, it will only be recalculated when the node is saved. That is, the computed field will be empty for nodes that already existed when you set up the field.

On a side note, the path alias is available as a field in Views; you don't need computed fields for this.

Excellent tute thanks, but I have a problem with Computed Field that I just can't find a way around.

I have set the field to display the user signature when they make a new item listing and this shows fine in Preview but is lost when viewed in a View. Saved to DB is selected.

Any thoughts much appreciated.

Gergely Lekli's picture

It's not exactly clear to me what you are trying to do, but if you are saying it works in the preview on the Views config page, and does not work when you actually view the View, then this could be related to the theme. The Views config page is normally displayed in the admin theme, but when viewing, you are switched to the site's theme.

Many thanks Gergely, sorry for not being clearer.

I have created a Content Type of Listing which allows site users to create item listings and display the new item in a view depending on the category they have selected. The Listing content Type has the computed field to fetch the users signature and add it to their new item. When a new item is being created and previewed the signature field displays in the node preview but not following save. The field is configured to display on the full content display only.
The site is at http://golf-flogger.com
I appreciate your help.

Rohan

Hello, I enjoyed your article. I wish to use this module for generating an auto incremental Staff ID. in an application am building. i will have the following fields;
field_country_code, -To capture the Country Code.
field_state_code, -To capture the State Code,
field_staff_initials, -To capture Staff Initials, then $count + 00001;
$count = db_select('node')->fields('node')->condition('type', $entity->type)->execute()->rowCount();
the expected output format is eg: NG/AB/OJ/0001. Pls can you guide me on a code that can achieve this.

Gergely Lekli's picture

I do not recommend this if you need a unique id. You are using the node count to generate the staff id, so if you ever happen to delete a node, you may end up with duplicate ids. Why don't you use the node id as the staff id?

Hi Gergely, thaks for your advice and very prompt response. Although i do not plan to ever allow deleting of this content type node, i will like to know if the node IDs generated can be for unique to specific content type as i have several other content types gnerating nodes on the site. Again, can you suggest how i may go about implementing your recommendation. I realy do appreciate your assistance, and your liberality of knowlage sharing. will i rrequire any specific module, or a custom one. Thanks.

Hi
Truly i think it is not a good idea going this to generate a staff ID...
A stronger and powerful way may be to Install the Serial module.
- add a serial field to you content type'
- add another field that with "Trim" option : ( May be trimming the node title)
-and now add a computed field (with saved checked) that compute "Trim field"+"Serial field";

NB: Please anybody can correct me if i am giving wrong advice so that i could learn more too.

Thanks

Intrestingly i was able to resolve my problem with this module....https://drupal.org/project/auto_entitylabel.
Thanks for your help.

Please provide a full example of using the module method. The reason I ask is that there are some things I am unsure of:

- In your example computed_field_field_profit_compute - what would you call the module?
- Are there any settings needed for the computed field in the admin config?
- What else is needed in the new module code for computed_field to recognise this function and call it?

Gergely Lekli's picture

This is pretty much a full example. You can put the function in any of your custom modules. Unlike regular Drupal hooks, this function's name does not contain your custom module's name.
No setting is needed; all you need to do is implement the function with the exact name that you see in the note under the 'Computed Code' textbox on the field config page, and, in that function, put your computed value into $entity_field[0]['value'].

Hello Gergely Lekli,

When I have a content type and I have a "field_a" and 'X' number published node from this content type and I want to sum all the 'X' number of 'field_a'. How can I do this? Is it possible to make it with the computed fields?

Thanx

Gergely Lekli's picture

Sure, you can do that. You'll need to create a new computed field to store the sum, say field_b, and implement the computed field function (which should be called computed_field_field_b_compute) for that field in your module. You will need to write code to calculate the sum in that function.

Can you help me?
I have created a select list field in my Entity Form.

Select List : none||none and AM||AM and PM||PM

In my computed field, I want to know what did the user chose by using IF STATEMENT

example:

if($halfday == 'AM')
{
//action
}
I tried to use array_pop (example code : $halfday = array_pop(field_get_items($entity_type,$entity,'field_halfday'));), but it does not work for me. What php code should I use in Computed field? Any suggestion?

Help me please :(

I have two fields. A "field_first_name" and a "field_last_name". I need to concatenate the two fields into a third field called "field_registry". Can you show me the Computed Code and the Display Code I have to insert?
Thanks for your help

Solution: $entity_field[0]['value'] = $entity->field_prezzo_prodotto[LANGUAGE_NONE][0]['value'] . $entity->field_costo_prodotto[LANGUAGE_NONE][0]['value'];

thank you for this great tutorial although I don't need some parts for example module dev. parts . It saved my life thanks!

Hi. Thank you so much for your post. Can you please take a look as how i can fix this issue https://www.drupal.org/node/2654982#comment-10777204

Thanks

Gergely Lekli's picture

Sure, I have added some thoughts to that issue on drupal.org.

Hi - One variation question...on Product Variations (PV) in Commerce Kickstart 2:

I need a computed field in my PV of a particular Content Type (CT).

This computed field results in a varchar string made up of components from PV and of the Content Type it belongs to.

// to get the PV..

$wrapper = entity_metadata_wrapper($entity_type, $entity);
// access field in PV
$field_my_gauge = $wrapper->field_needle_gauge->value();
// try to access field in CT of PV... the line below fails
$field_my_type = $wrapper->field_needle_type->value();

So, how do I properly access the CT fields?

Thanks!

How do you get the values of an entity reference field?

Post new comment