drupal-9computed-field

calculate Computed Field from datetime-range/time-range field using custom module hook


OK, I've been struggling with this for days now. Hopefully someone can shed some light on this...

Site is on Drupal 9.4.3 (currently the latest) using PHP 8.1.6

I have a content type with a datetime_range field setup to use as time-range (Start time and End Time) formatted for time only input. Field Name: field_friday_hours

The content type also has a computed_field decimal field, where I want to set the total hours calculated from the "Start time and End time" of the date range field. Computed Field Name: field_test_total_hours

The date range field collecting the start and stop time also uses time_range contrib module as well as DateTime Hide Seconds (datetimehideseconds) module - which I don't know if that's been complicating the situation for me or if that doesn't matter.

The computed field gives me this code sample to use in a custom module: The hook implementation function signature should be

computed_field_field_test_total_hours_compute($entity_type_manager, $entity, $fields, $delta)

and the desired value should be returned. The variables available to your code include:

$entity_type_manager: The entity type manager.
$entity: The entity the field belongs to.
$fields: The list of fields available in this entity.
$delta: Current index of the field in case of multi-value computed fields (counting from 0).
$value: The resulting value to be set above, or returned in your hook implementation).

Here is what dpm($form); gives me on node/add/...(

[field_friday_hours] => stdClass Object
                                        (
                                            [__CLASS__] => Drupal\field\Entity\FieldConfig
                                            [entityTypeId:protected] => field_config
                                            [enforceIsNew:protected] => 
                                            [typedData:protected] => 
                                            [cacheContexts:protected] => Array(0)
                                            [cacheTags:protected] => Array(0)
                                            [cacheMaxAge:protected] => -1
                                            [_serviceIds:protected] => Array(0)
                                            [_entityStorages:protected] => Array(0)
                                            [originalId:protected] => node.time_sheets.field_friday_hours
                                            [status:protected] => 1
                                            [uuid:protected] => fad1e2cf-9c60-4e4a-b171-33c2579f6712
                                            [isUninstalling:Drupal\Core\Config\Entity\ConfigEntityBase:private] => 
                                            [langcode:protected] => en
                                            [third_party_settings:protected] => Array(0)
                                            [_core:protected] => Array(0)
                                            [trustedData:protected] => 
                                            [dependencies:protected] => Array(2)
                                            [isSyncing:protected] => 
                                            [id:protected] => node.time_sheets.field_friday_hours
                                            [field_name:protected] => field_friday_hours
                                            [field_type:protected] => daterange
                                            [entity_type:protected] => node
                                            [bundle:protected] => time_sheets
                                            [label:protected] => Friday Hours
                                            [description:protected] => 
                                            [settings:protected] => Array(0)
                                            [required:protected] => 
                                            [translatable:protected] => 
                                            [default_value:protected] => Array(0)
                                            [default_value_callback:protected] => 
                                            [fieldStorage:protected] => Drupal\field\Entity\FieldStorageConfig
                                            [itemDefinition:protected] => Drupal\Core\Field\TypedData\FieldItemDataDefinition
                                            [constraints:protected] => Array(0)
                                            [propertyConstraints:protected] => Array(0)
                                            [deleted:protected] => 
                                        )
[field_test_total_hours] => stdClass Object
                                        (
                                            [__CLASS__] => Drupal\field\Entity\FieldConfig
                                            [entityTypeId:protected] => field_config
                                            [enforceIsNew:protected] => 
                                            [typedData:protected] => 
                                            [cacheContexts:protected] => Array(0)
                                            [cacheTags:protected] => Array(0)
                                            [cacheMaxAge:protected] => -1
                                            [_serviceIds:protected] => Array(0)
                                            [_entityStorages:protected] => Array(0)
                                            [originalId:protected] => node.time_sheets.field_test_total_hours
                                            [status:protected] => 1
                                            [uuid:protected] => a7179c06-3ea9-4152-960d-9f82075e7da0
                                            [isUninstalling:Drupal\Core\Config\Entity\ConfigEntityBase:private] => 
                                            [langcode:protected] => en
                                            [third_party_settings:protected] => Array(0)
                                            [_core:protected] => Array(0)
                                            [trustedData:protected] => 
                                            [dependencies:protected] => Array(2)
                                            [isSyncing:protected] => 
                                            [id:protected] => node.time_sheets.field_test_total_hours
                                            [field_name:protected] => field_test_total_hours
                                            [field_type:protected] => computed_decimal
                                            [entity_type:protected] => node
                                            [bundle:protected] => time_sheets
                                            [label:protected] => test total hours
                                            [description:protected] => 
                                            [settings:protected] => Array(2)
                                            [required:protected] => 
                                            [translatable:protected] => 
                                            [default_value:protected] => Array(0)
                                            [default_value_callback:protected] => 
                                            [fieldStorage:protected] => Drupal\field\Entity\FieldStorageConfig
                                            [itemDefinition:protected] => Drupal\Core\Field\TypedData\FieldItemDataDefinition
                                            [constraints:protected] => Array(0)
                                            [propertyConstraints:protected] => Array(0)
                                            [deleted:protected] => 
                                        )
[field_friday_hours] => Array
                                        (
                                            [type] => time_range
                                            [weight] => 26
                                            [region] => content
                                            [settings] => Array(0)
                                            [third_party_settings] => Array(1)
                                        )
[field_test_total_hours] => Array
                                        (
                                            [type] => computed_number_widget
                                            [weight] => 27
                                            [region] => content
                                            [settings] => Array(0)
                                            [third_party_settings] => Array(0)
                                        )
[field_friday_hours] => stdClass Object
                                        (
                                            [__CLASS__] => Drupal\time_range\Plugin\Field\FieldWidget\TimeRangeWidget
                                            [pluginId:protected] => time_range
                                            [pluginDefinition:protected] => Array(6)
                                            [configuration:protected] => Array(0)
                                            [stringTranslation:protected] => Drupal\Core\StringTranslation\TranslationManager
                                            [_serviceIds:protected] => Array(0)
                                            [_entityStorages:protected] => Array(0)
                                            [messenger:protected] => 
                                            [settings:protected] => Array(0)
                                            [thirdPartySettings:protected] => Array(1)
                                            [defaultSettingsMerged:protected] => 
                                            [fieldDefinition:protected] => Drupal\field\Entity\FieldConfig
                                            [dateStorage:protected] => Drupal\Core\Config\Entity\ConfigEntityStorage
                                        )
[field_test_total_hours] => stdClass Object
                                        (
                                            [__CLASS__] => Drupal\computed_field\Plugin\Field\FieldWidget\ComputedNumberWidget
                                            [pluginId:protected] => computed_number_widget
                                            [pluginDefinition:protected] => Array(6)
                                            [configuration:protected] => Array(0)
                                            [stringTranslation:protected] => Drupal\Core\StringTranslation\TranslationManager
                                            [_serviceIds:protected] => Array(0)
                                            [_entityStorages:protected] => Array(0)
                                            [messenger:protected] => 
                                            [settings:protected] => Array(0)
                                            [thirdPartySettings:protected] => Array(0)
                                            [defaultSettingsMerged:protected] => 
                                            [fieldDefinition:protected] => Drupal\field\Entity\FieldConfig
                                            [default_value] => 
                                        )
[field_friday_hours] => Array
        (
            [#type] => container
            [#parents] => Array
                (
                    [0] => field_friday_hours_wrapper
                )

            [#attributes] => Array
                (
                    [class] => Array
                        (
                            [0] => field--type-daterange
                            [1] => field--name-field-friday-hours
                            [2] => field--widget-time-range
                        )

                )

            [widget] => Array
                (
                    [0] => Array
                        (
                            [#title] => Friday Hours
                            [#title_display] => before
                            [#description] => 
                            [#field_parents] => Array
                                (
                                )

                            [#required] => 
                            [#delta] => 0
                            [#weight] => 0
                            [value] => Array
                                (
                                    [#type] => datetime
                                    [#default_value] => 
                                    [#date_increment] => 1
                                    [#date_timezone] => America/New_York
                                    [#required] => 
                                    [#title] => stdClass Object
                                        (
                                            [__CLASS__] => Drupal\Core\StringTranslation\TranslatableMarkup
                                            [string:protected] => Start time
                                            [arguments:protected] => Array(0)
                                            [translatedMarkup:protected] => 
                                            [options:protected] => Array(0)
                                            [stringTranslation:protected] => Drupal\Core\StringTranslation\TranslationManager
                                        )

                                    [#date_date_format] => none
                                    [#date_date_element] => none
                                    [#date_date_callbacks] => Array
                                        (
                                        )

                                    [#date_time_format] => H:i:s
                                    [#date_time_element] => time
                                    [#date_time_callbacks] => Array
                                        (
                                        )

                                    [#datetimehideseconds] => Array
                                        (
                                            [hide] => 1
                                        )

                                )

                            [#theme_wrappers] => Array
                                (
                                    [0] => fieldset
                                )

                            [#element_validate] => Array
                                (
                                    [0] => Array
                                        (
                                            [0] => Drupal\time_range\Plugin\Field\FieldWidget\TimeRangeWidget
                                            [1] => validateStartEnd
                                        )

                                    [1] => Array
                                        (
                                            [0] => Drupal\time_range\Plugin\Field\FieldWidget\TimeRangeWidget
                                            [1] => validateStartEnd
                                        )

                                )

                            [end_value] => Array
                                (
                                    [#title] => stdClass Object
                                        (
                                            [__CLASS__] => Drupal\Core\StringTranslation\TranslatableMarkup
                                            [string:protected] => End time
                                            [arguments:protected] => Array(0)
                                            [translatedMarkup:protected] => 
                                            [options:protected] => Array(0)
                                            [stringTranslation:protected] => Drupal\Core\StringTranslation\TranslationManager
                                        )

                                    [#type] => datetime
                                    [#default_value] => 
                                    [#date_increment] => 1
                                    [#date_timezone] => America/New_York
                                    [#required] => 
                                    [#date_date_format] => none
                                    [#date_date_element] => none
                                    [#date_date_callbacks] => Array
                                        (
                                        )

                                    [#date_time_format] => H:i:s
                                    [#date_time_element] => time
                                    [#date_time_callbacks] => Array
                                        (
                                        )

                                    [#datetimehideseconds] => Array
                                        (
                                            [hide] => 1
                                        )

                                )

                        )

                    [#theme] => field_multiple_value_form
                    [#field_name] => field_friday_hours
                    [#cardinality] => 1
                    [#cardinality_multiple] => 
                    [#required] => 
                    [#title] => Friday Hours
                    [#description] => 
                    [#max_delta] => 0
                    [#after_build] => Array
                        (
                            [0] => Array
                                (
                                    [0] => Drupal\time_range\Plugin\Field\FieldWidget\TimeRangeWidget
                                    [1] => afterBuild
                                )

                        )

                    [#field_parents] => Array
                        (
                        )

                    [#parents] => Array
                        (
                            [0] => field_friday_hours
                        )

                    [#tree] => 1
                )

            [#access] => 1
            [#weight] => 26
            [#cache] => Array
                (
                    [contexts] => Array
                        (
                        )

                    [tags] => Array
                        (
                            [0] => config:field.field.node.time_sheets.field_friday_hours
                            [1] => config:field.storage.node.field_friday_hours
                        )

                    [max-age] => -1
                )

        )
[field_test_total_hours] => Array
        (
            [#type] => container
            [#parents] => Array
                (
                    [0] => field_test_total_hours_wrapper
                )

            [#attributes] => Array
                (
                    [class] => Array
                        (
                            [0] => field--type-computed-decimal
                            [1] => field--name-field-test-total-hours
                            [2] => field--widget-computed-number-widget
                        )

                )

            [widget] => Array
                (
                    [0] => Array
                        (
                            [value] => Array
                                (
                                    [#title] => field_test_total_hours
                                    [#type] => hidden
                                    [#default_value] => 0
                                    [#disabled] => 1
                                    [#description] => stdClass Object
                                        (
                                            [__CLASS__] => Drupal\Core\StringTranslation\TranslatableMarkup
                                            [string:protected] => Normally this field should not be shown!
                                            [arguments:protected] => Array(0)
                                            [translatedMarkup:protected] => 
                                            [options:protected] => Array(0)
                                            [stringTranslation:protected] => Drupal\Core\StringTranslation\TranslationManager
                                        )

                                )

                        )

                    [#theme] => field_multiple_value_form
                    [#field_name] => field_test_total_hours
                    [#cardinality] => 1
                    [#cardinality_multiple] => 
                    [#required] => 
                    [#title] => test total hours
                    [#description] => 
                    [#max_delta] => 0
                    [#after_build] => Array
                        (
                            [0] => Array
                                (
                                    [0] => Drupal\computed_field\Plugin\Field\FieldWidget\ComputedNumberWidget
                                    [1] => afterBuild
                                )

                        )

                    [#field_parents] => Array
                        (
                        )

                    [#parents] => Array
                        (
                            [0] => field_test_total_hours
                        )

                    [#tree] => 1
                )

            [#access] => 1
            [#weight] => 27
            [#cache] => Array
                (
                    [contexts] => Array
                        (
                        )

                    [tags] => Array
                        (
                            [0] => config:field.field.node.time_sheets.field_test_total_hours
                            [1] => config:field.storage.node.field_test_total_hours
                        )

                    [max-age] => -1
                )

        )

In my custom module I've tried a bunch of random things found in other posts that yield either no results or produces errors.

I've tried

function computed_field_field_test_total_hours_compute($entity_type_manager, $entity, $fields, $delta) {
  $start_time = $entity->field_friday_hours[0]['value'];
  $end_time = $entity->field_friday_hours[0]['end_value'];
  $total_time = $end_time - $start_time;
  
  $value = $total_time;
}

additionally I've tried to get the time values with: (examples for $start_time...)

$start_time = $fields['field_friday_hours'][0]['value']['time']['#value'];
$start_time = $entity->field_friday_hours[0]['value']['#default_value'];
$start_time = $entity->get('field_friday_hours')->getValue();
$start_time = $field['field_friday_hours'][0]['value']['time']['#value'];

Further more even trying to just set the value of the computed field using something like $value = 8.5; without trying to get any other field data/values and it won't even set it. So I'm assuming I missed the boat on this completely.

I know with computed field there is also the option to format the computed field via a plugin and doing calculations there - however before I even attempt that approach I'd like to know how to make the hook approach work correctly.

I even tried hook_alter like this:

function custom_module_computed_field_field_test_total_hours_value_alter(&$value, $context) {

}

But nothing there seamed to work either!

At this point my head is pounding from reviewing document after document having searched for an answer extensively with nothing adding up and or working.

If recreating the the computed field in another type would help I'm all for it - or if there is a definite easier way to do this I'm all ears...

Please HELP - Thanks in advance!


Solution

  • So after a lot of trial and error I finally figured things out.

    With the use of the provided function displayed on the computed fields edit page in my custom module as follows:

    function computed_field_field_test_total_hours_compute($entity_type_manager, $entity, $fields, $delta) {
    // Get and set Start time
      $get_stime = $entity->field_friday_hours->value;
      $start_time = new DrupalDateTime($get_stime);
    
    // Get and set End time
      $get_etime = $entity->get('field_friday_hours')->getValue()[0]['end_value'];
      $end_time = new DrupalDateTime($get_etime);
    
    // Calculate hours to decimal
      $diff = $end_time->getTimestamp() - $start_time->getTimestamp();
      $hours = $diff/3600;
      $tot_hours = round($hours, 2);
    
    // Set Computed field value
      $value = $tot_hours;
      return $value;
    }
    

    In comparison to what I was originally trying (which was initially based on other posts and answers to similar questions when trying to get field values in a hook or function) I was close. Looks like I didn't have enough information with setting a variable with $entity or it was in the wrong format.

    I'm still not 100% clear on what patterns in what types of hooks can be used to get field data especially in Drupal 8 and 9, let alone through the use of a custom module. Most documentation especially for contributed modules for anything Drupal 8 or 9 is incomplete or none existent.

    I ended up having to simplifying things first and work through it.

    At this time what confused me the most was were I could get the start time with this:

    $entity->field_friday_hours->value;
    

    To get the end time value I couldn't do this:

    $entity->field_friday_hours->end_value;
    

    I had to do this:

    $entity->get('field_friday_hours')->getValue()[0]['end_value'];
    

    Which at first struck me as odd; anytime I tried to use get() or getValue() I constantly fatal errored with can't get or getValue of null. So for the longest time I really thought that wouldn't work in either the suggested function or hook_value_alter.

    That said using dpm($form) made how it ended up working even a bit more confusing, considering that should have given me the structure to use to get the start and end time values "at least from what I understood" and had read pretty much everywhere.

    If I'm understanding correctly now get() gets the field and getValue() starts from whatever part of the object array that follows it - which is still a tad bit confusing (aside from the fact I may be completely wrong on that, as I'm just going by what I can see happening as I worked through everything)

    Please feel free to correct me on anything.

    Regardless this works in Drupal 9 to get date-range start and end times and then convert them to hours in a decimal format and return them to a computed field.