1

I have a content type with the field_watermelons and field_apples fields. I want to record the sum of these fields in the (existing) field_total_fruits field. How can I achieve this?

I do not want to use the Computed field module(because of PHP filter requirement) and the Math Field module is also out because it can't handle Field Collections at the moment (if it could, then I'd have my answer).

I tried hook_form_alter with a callback function attached to the $form['submit']. In that callback, I've tried a number of methods of inserting new values including adding values to $form_state's input, values, completed_form. I've also tried to use entity_metadata_wrapper() approaches in the callback, to no avail. I also tried form_set_value().

How to create a Rules action set to calculate a sum of integers in a Field Collection? looks interesting, but there's no answer.

Surely I'm over-thinking this. Can someone tell me if there's a simple way to sum two field values and insert that sum into the value of another field programmatically?

I suppose I could use the Drupal AJAX system too. I just didn't want that hassle, thinking that there's an easier way, but perhaps it's the best option.

  • Comment have been removed. Please don't use them for extended discussion; instead, add the relevant parts to either the question or any of the answers. – kiamlaluno Apr 1 '17 at 12:36
4

Ok, I've got a solution that will work for me. This d.o forum comment was very helpful.

Instead of changing the $form['submit'] array, prepend the callback to the submit callback stack! Like so:

array_unshift($form['actions']['submit']['#submit'], 'MODULE_my_custom_submit_handler');

Note that the code in the comment from d.o is a bit off, you don't need to assign the unshift'ed array, you just run array_unshift on the array itself.

Once that's added in your hook_form_alter you can do WHATEVER processing magic you want with form_set_value(). Cool! Thanks all.

EDIT: here's what the callback looks like. Also note that for some reason someone took out my fields in my original question. But these field names would make more sense if someone didn't do that.

function MODULE_my_custom_submit_handler($form, &$form_state) {
  $lang = $form['language']['#value'];

  $a = $form_state['values']['field_fruit_collection'][$lang][0]['field_watermelon']['und'][0]['value'];
  $b = $form_state['values']['field_fruit_collection'][$lang][0]['field_apples']['und'][0]['value'];

  $sum = $a + $b;
  form_set_value($form['field_total_fruit'][$lang][0]['value'], $sum, $form_state);
}
  • vote up, this is better and simpler solution. – Yusef Mar 28 '17 at 20:39
  • 1
    Yes this is, in hindsight, simpler to work with. The fields being stored are redundant, but so what? Now you have this data available as fields, which can be useful along the way. – donquixote Mar 30 '17 at 15:07
  • 1
    However, I would suggest using hook_entity_presave() instead of hook_form_alter(). Consider that a node or entity can be updated outside the usual edit form! With hook_entity_presave() you can be sure that your logic always applies (unless someone writes directly with the database, skipping Drupal APIs) – donquixote Mar 30 '17 at 15:10
  • @donquixote very good point, I will look into hook_entity_presave() thanks! – cdmo Mar 30 '17 at 15:16
  • 1
    I added a section to my own answer. – donquixote Mar 30 '17 at 15:43
3

If the value of the field_total_fruits is always the sum of the other fields, then there is not really a point in storing it. Instead, you can compute the value when you want to display it, or when you want to do other things with it.

You now have a number of options, depending what you want to do with the result.

I personally do not like solutions like "computed field", from what I remember from last time I used it.

Actual fields + hook_entity_presave() (EDIT)

Yes, you can store the sum in a dedicated field added to the entity. This means having redundant data in the database, but it also means having the data available to functionality that only works with fields. It can make your life easier.

To do so, you can use hook_entity_presave() to calculate the sum and store it in the new field.

Notice that you probably need to load the field collection items, because the field items of $node->field_fruit_collection will only contain the field collection item ids.

You should pay attention to the array indices [$langcode][$delta] of field items. On a specific site it can be sufficient to always hardcode `[LANGUAGE_NONE][0], but this limits the reusability of the module a lot. And you'd still need to check if the keys are set to prevent PHP notices.

I think it is ok to hardcode some assumptions into your module. You are already hardcoding the field names, so you can also hardcode which of the fields are single-value. So in [$langcode][$delta], the $delta is always 0. Or use reset() to pick the first item. This makes your module unsuitable for publishing on drupal.org, but good enough for your site.

But for the language key you should be more careful. Normally I would say loop over the languages, then store results per language in the field_total_fruit. Maybe use some kind of fallback logic to use LANGUAGE_NONE if no language present.

What complicates things here, is that both the field collection and the field_apples and field_watermelons can have values per language. E.g. you could have $node->field_fruit_collection['fr'][0]['field_collection_entity']->field_apples['en'][0]['value'].

Things like this can go unnoticed for a while, and then you get strange bug reports that make you scratch your head.

Since all of this is numbers, I'd say configure these fields so that only LANGUAGE_NONE is used.

I would say do NOT use field_get_items() here. Or if you do, be careful what you are doing. The entity will be saved in all languages, and field_get_items() filters the result to the current language only. So you could have inconsistent behavior depending on the current user language.

Entity properties

You could use hook_entity_property_info() to register a property with a value callback. In this value callback you would use field_get_items() on both fields and calculate the sum.

Entity properties can be used in a number of places, but they do not naturally show up in field ui for entity view modes, or as views fields.

You would need to do more research to find a way to make them show up in views or elsewhere. Although the hook has some interesting fields like 'entity views field' which might help.

I think it would be worth it to do some more research in this direction, possibly even open a new question. Entity properties are quite a powerful concept, and really there has to be a way to make them show up where you need them.

DS custom field

You could declare a Display suite custom field where you calculate the value. This will only be available in DisplaySuite-enabled view modes, though.

Custom Views field handler

You could write you own views field handler. But these are only available in Views, not elsewhere.

I personally have created some of those, but never really enjoyed working with the API.

Custom "entity display plugin"

There is a collection of modules that I have created myself, that would make this specific task quite easy. The downside is that this is an entire new world to dive into, not something you want to do for just one little task. I am presenting this option here for completeness.

You start with EntDisP. It has a bunch of dependencies, which are listed on the module page. The basic getting-started is explained on the module page.

You would then create a custom module, e.g. "fruitsum" with a custom entity display plugin.

I could elaborate on this further, but this would give it unjust priority over the other options.

The advantage is that these plugins are easy to write and maintain once you get the hang of it, and they can be used in a lot of places.

The disadvantage is that, as said, this is a completely new world to dive into.

Conclusion

I personally would suggest to follow the "entity property" route, with some research of your own. If you find a way to make those things show up, you have a new tool at your disposal for future tasks.

I am even considering to create a new entdisp plugin for the purpose of displaying properties.

Maybe other answers can elaborate more on this direction. Or if you find out, you could write the answer yourself!

  • 1
    Thanks for the answer. I'm not sure any of these will be a good way forward for me, but I appreciate the ideas! – cdmo Mar 28 '17 at 12:11
1

So you want to sum 2 fields of a field collection, and you think the Rules module may help you doing so. Indeed, that's a way to do so, have a look at my answer to "How to add up all items of a field collection field using Rules?" which includes a rule (in Rules export format). Here is how the rule looks like:

{ "rules_calculate_sum_of_prices_in_all_field_collection_items" : {
    "LABEL" : "Calculate sum of prices in all field collection items",
    "PLUGIN" : "reaction rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules" ],
    "ON" : { "node_view--article" : { "bundle" : "article" } },
    "IF" : [
      { "entity_has_field" : { "entity" : [ "node" ], "field" : "field_article_details" } }
    ],
    "DO" : [
      { "drupal_message" : { "message" : "\u003Cstrong\u003EDrupal calculator\u003C\/strong\u003E started ..." } },
      { "variable_add" : {
          "USING" : { "type" : "decimal", "value" : "0" },
          "PROVIDE" : { "variable_added" : { "total_price" : "Price total" } }
        }
      },
      { "LOOP" : {
          "USING" : { "list" : [ "node:field-article-details" ] },
          "ITEM" : { "article_details_item" : "Article details item" },
          "DO" : [
            { "data_calc" : {
                "USING" : {
                  "input_1" : [ "total-price" ],
                  "op" : "+",
                  "input_2" : [ "article-details-item:field-price" ]
                },
                "PROVIDE" : { "result" : { "calculation_result" : "Calculation result" } }
              }
            },
            { "data_set" : { "data" : [ "total-price" ], "value" : [ "calculation-result" ] } },
            { "drupal_message" : { "message" : "After adding a price of \u003Cstrong\u003E[article-details-item:field-price]\u003C\/strong\u003E for field collection item with id \u003Cstrong\u003E[article-details-item:item-id]\u003C\/strong\u003E, subtotal is \u003Cstrong\u003E[calculation-result:value]\u003C\/strong\u003E." } }
          ]
        }
      },
      { "drupal_message" : { "message" : "The \u003Cstrong\u003ETotal price\u003C\/strong\u003E for all prices included as field collection items is \u003Cstrong\u003E[total-price:value]\u003C\/strong\u003E." } },
      { "drupal_message" : { "message" : "\u003Cstrong\u003EDrupal calculator\u003C\/strong\u003E ended ..." } }
    ]
  }
}

Refer to the related answer for "some" details, and a demo, about it.

To "insert that sum into the value of another field" (as in your question), just add another Rules action after the sum got calculated, and use a "Set a data value" Rules Action.

PS 1: I'm assuming this question is about D7.

PS 2: this answer applies to revision 2 of your question, and does not yet take into consideration your most recent edit you added (and which makes your question a chameleon question, which IMO should have been a followup question ... instead of changing your original question).

  • 1
    So... this is interesting. I did tag this question 7 and another user, @kiamlaluno, removed it. And I think that user (or is it a group of users?) is a moderator. So, what's the way to indicate a D7 question? Confused. – cdmo Mar 28 '17 at 20:08
  • 1
    will do. I must say I'm not real happy with the heavy editing of the body of my question either. 🤔 – cdmo Mar 28 '17 at 20:19
  • I got this when I tried to import Import failed. Unable to parse the pasted export. But I naively didn't change any of the field names... let me see if I that fixes it. – cdmo Mar 28 '17 at 20:24
  • @cdmo my mistake, tiny copy/paste error from the related answer (let me fix it quickly ... at the very begiinning it's missing an open- parent) ... – Pierre.Vriens Mar 28 '17 at 20:27
  • 1
    I am going too fast, sorry. Digesting your full answer in the other post now, it is really impressive. Something about this approach does seem more sustainable to me. Rules is probably more universal than a callback on a hook form alter. I'm hard up against a deadline at the moment, (when am I not come to think of it) but I will implement this and give it a shot soon. – cdmo Mar 28 '17 at 20:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.