3

I have created a computed field (computed_field module) and I'd like the calculated value of this field to be computed on the fly for each of my views results.

Specifically, I have a start time, and and end time for each "Offer" (content type) in my view. I have created a computed field that determines whether the current time is between the start time, and end time, and then outputs a string value of "now" or "ending_soon" or "starting_soon" or "default" (if it's outside the parameters). I'm doing this all because I need to use the contents of this calculated field as a path variable for custom map markers in a google map (different 'pins' for offers/deals that are happening now, vs ones that are happen earlier or later in the day).

inside my THEMENAME.theme file:

function computed_field_field_now_compute($entity_type_manager, $entity, $fields, $delta)
{
  // from the database:
  $timestamp = $fields['field_business_dates'][0]['value'] ?? "";
  $endtimestamp = $fields['field_business_dates'][0]['end_value'] ?? "";

  $now = gmdate('Y-m-d\TH:i:s\Z');
  $utc = new DateTimeZone('UTC');
  $startDate = new DateTime($timestamp, $utc);
  $endDate = new DateTime($endtimestamp, $utc);
  $nowDate = new DateTime($now, $utc);
  $startSwatch = date_format($startDate, 'B');
  $endSwatch = date_format($endDate, 'B');
  $nowSwatch = date_format($nowDate,'B');
  $cutOffSwatch = 22; // Note: 41.6 = 1 hour;
  $endingSoonTime = $endSwatch - $cutOffSwatch;
  $startingSoonTime = $startSwatch - $cutOffSwatch;
  $value = "default";

  if ($nowSwatch >= $startSwatch && $nowSwatch <= $endSwatch) {
    if ($nowSwatch >= $endingSoonTime) {
      $value = "ending_soon";
    } else {
      $value = "now";
    }
  } else if ($nowSwatch >= $startingSoonTime && $nowSwatch <= $startSwatch) {
    $value = "starting_soon";
  } else {
    // return default value
  }
  return $value;
}

As these calculations are time-sensitive, I've turned off caching with the hope that the values are calculated on the fly and displayed in my view. But they are not displayed in views, and don't seem to display at all unless I save each individual node first. As I have over 6500 nodes...I don't see this as feasible. How can I ensure that when a view needs to display the calculated field, that the calculation is executed and rendered?

6
  • Does this answer your question? How to resave all my nodes
    – leymannx
    Commented Nov 3, 2020 at 22:33
  • But maybe I don't understand the problem and maybe you can edit your question and add a user story or some more examples of what you want to achieve exactly then? To let us figure out any alternative approach.
    – leymannx
    Commented Nov 3, 2020 at 22:38
  • My concern with that is I want the values to be calculated when the VIEW that calls fields from the node is displayed, not just when the node is saved. And I did try views bulk operation of saving all the nodes, but it seems that my hook isn't called on views operations (only on node saves).
    – Doomd
    Commented Nov 3, 2020 at 22:39
  • I don't think is a good idea to update nodes when they are displayed on a view. That would make the view rather heavy in performance terms, and it will be updating nodes when is not needed (once they are updated you must provide a way to detect they are already updated or they will be updated again and again). Besty way is to update all programmatically and then update on node save.
    – sanzante
    Commented Nov 4, 2020 at 6:42
  • 4
    HOWEVER, seeing your code, the data you want to update is dependant on time, so there's no point to store the value on the node and update it on node save or view execution. I guess that value must be calculated on the fly when the node is viewed (full view, teaser, displayed on a view, etc), or even by the JS layer.
    – sanzante
    Commented Nov 4, 2020 at 6:44

1 Answer 1

5

"How do I output a computed field value in real time?"

The answer is that you cannot. The Computed Field module updates the value on entity save. So, to get the most recent value, you have to save the entity.

If you are trying to show a value that you compute in a view, computed field is a bad solution because you will have to re-save all the entities whenever you display the view, which is horrible for performance.

Alternative solution idea

Since you're doing calculations based on time, one option might be to use Views filters. Make a primary view that shows the default pins, and then embed or attach additional views that have filters that are set to find the pins for ending_soon and starting_soon.

If you need to override the Views filter calculations, you can do that in hook_views_pre_view():

Here's a code example from one of my sites where I override the filter value to use "midnight for the logged-in user's timezone)."

function MYMODULE_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() === 'my_view' && ($display_id === 'my_display') {
    $filters = $view->display_handler->getOption('filters');
    if ($filters['field_datetime_target_value']) {
      $user_timezone = date_default_timezone_get();
      $current_timestamp = \Drupal::time()->getCurrentTime();
      $midnight_usertimezone_datestring = MYMODULE_get_midnight_datestring("$current_timestamp");

      $filters['field_datetime_target_value']['value']['value'] = $midnight_usertimezone_datestring;
    }
    $view->display_handler->overrideOption('filters', $filters);
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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