Problem/Motivation

Render entity with Datetime Range field throws error:

Error: Unsupported operand types in Drupal\datetime_range\Plugin\Field\FieldFormatter\DateRangeDefaultFormatter->viewElements() (line 64 of core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php).

Proposed resolution

Needs to initialize array before assignment.

Remaining tasks

N/A

User interface changes

N/A

API changes

N/A

Data model changes

N/A

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

paulanders created an issue. See original summary.

mpdonadio’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Can you confirm what version of PHP you saw this with? We also need a test to demonstrate this error. If this is a problem we should also be seeing it with DateTimeDefaultFormatter and GenericFileFormatter.

iamdroid’s picture

I catch this error on php 7.0.8.

I just added Datetime Range field to node type and set the value to some nodes. Then nodes with date range setted don't loading more.

In patch I added one line that has been missed:

$elements[$delta] += array('#attributes' => array());

In GenericFileFormatter It exists, and in DateTimeDefaultFormatter attributes array defines in block above.

mpdonadio’s picture

Assigned: Unassigned » mpdonadio

Will look at this tonight. A bit baffled why this hasn't popped up with any of the automated testing since this was committed to 8.2 and 8.3.

mpdonadio’s picture

Assigned: mpdonadio » Unassigned

Ok, this isn't showing in tests up because with a standard profile w/o any extra modules, `$item->_attributes` is always empty, so the innards of the if never get executed.

+++ b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php
@@ -61,6 +61,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
         if (!empty($item->_attributes)) {
+          $elements[$delta] += array('#attributes' => array());
           $elements[$delta]['#attributes'] += $item->_attributes;
           // Unset field item attributes since they have been included in the
           // formatter output and should not be rendered in the field template.
 

This can probably be simplified to

         if (!empty($item->_attributes)) {
           $elements[$delta]['#attributes'] = $item->_attributes;

since we aren't seeing the #attributes above, and $item->_attributes should be an array.

We are going to probably need a test to demonstrate bug and to prevent a regression (especially since this will be an area with upcoming refactoring).

mpdonadio’s picture

Kudos to @tim.plunkett for pointing me in the right direction here.

The test would essentially need to add to entity_test_entity_prepare_view() to add an attribute to a daterange field on entity_test entity, that we can then check in our own test. Wedging this into DateRangeFieldTest() isn't easy since it uses random machine names, so I think we want a separate test class for this (ideally a kernel test).

mpdonadio’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
6.01 KB
7.28 KB
7.34 KB

We don't have a wrapper (yet) around daterange fields. So, this patch lets the $item->_attributes propagate to the field wrapper on proper ranges, but be on the item when start and end are the same.

This test change makes me cry a little, but the hook_entity_prepare_view() wasn't firing from DateTestBase::renderTestEntity().

Edit: this patch is best read applied. The actual change to DateRangeDefaultFormatter is a little hard to interpret just from the diff itself.

The last submitted patch, 7: 2811725-test-only.patch, failed testing.

jhedstrom’s picture

+++ b/core/modules/datetime_range/src/Tests/DateRangeFieldTest.php
@@ -140,6 +140,10 @@ public function testDateRangeField() {
+      // Verify that hook_entity_prepare_view can add attributes.
+      $this->drupalGet('entity_test/' . $id);
+      $this->assertFieldByXPath('//div[@data-field-item-attr="foobar"]');

@@ -203,6 +207,10 @@ public function testDateRangeField() {
+      // Verify that hook_entity_prepare_view can add attributes.
+      $this->drupalGet('entity_test/' . $id);
+      $this->assertFieldByXPath('//time[@data-field-item-attr="foobar"]');

@@ -289,6 +297,10 @@ public function testDatetimeRangeField() {
+    // Verify that hook_entity_prepare_view can add attributes.
+    $this->drupalGet('entity_test/' . $id);
+    $this->assertFieldByXPath('//div[@data-field-item-attr="foobar"]');

@@ -360,6 +372,10 @@ public function testDatetimeRangeField() {
+    // Verify that hook_entity_prepare_view can add attributes.
+    $this->drupalGet('entity_test/' . $id);
+    $this->assertFieldByXPath('//time[@data-field-item-attr="foobar"]');

@@ -440,6 +456,10 @@ public function testAlldayRangeField() {
+    // Verify that hook_entity_prepare_view can add attributes.
+    $this->drupalGet('entity_test/' . $id);
+    $this->assertFieldByXPath('//div[@data-field-item-attr="foobar"]');

@@ -513,6 +533,10 @@ public function testAlldayRangeField() {
+    // Verify that hook_entity_prepare_view can add attributes.
+    $this->drupalGet('entity_test/' . $id);
+    $this->assertFieldByXPath('//div[@data-field-item-attr="foobar"]');
+

Can a @see entity_test_entity_prepare_view() note be added here, so in the future it's easy to find the hook responsible for the data in these tests?

This test change makes me cry a little, but the hook_entity_prepare_view() wasn't firing from DateTestBase::renderTestEntity().

I might be missing something here, but what changed that is now making it fire? (Edit, I see now--the changes to the test)

mpdonadio’s picture

FileSize
7.55 KB
2.46 KB

#9, good idea.

As a followup, and part of the long term plans we really need to refactor these tests (and the datetime ones) into unit/kernel/btb as necessary, and item/widgets/formatters. These are pretty messy right now, and they are way to big to manage.

jhedstrom’s picture

Status: Needs review » Reviewed & tested by the community

This looks good to go. Refactoring tests can perhaps happen during the conversion/split from webtestbase to browsertestbase + kernel?

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed 36bc912 to 8.3.x and 1137253 to 8.2.x. Thanks!

  • alexpott committed 36bc912 on 8.3.x
    Issue #2811725 by mpdonadio, paulanders, jhedstrom: Error when render...

  • alexpott committed 1137253 on 8.2.x
    Issue #2811725 by mpdonadio, paulanders, jhedstrom: Error when render...
iamdroid’s picture

Status: Fixed » Active

Unfortunately this commit doesn't fixes the issue.
I don't see any changes in DateRangeDefaultFormatter.php that changes behavior for #attributes array, and $item->_attributes still concatenates with undefined array.
And this behaviour causes this error.

Actually I think, that this situation exist because this operation was missed during copy paste.
In GenericFileFormatter there is code for define array before concatention:

$elements[$delta] += array('#attributes' => array());
$elements[$delta]['#attributes'] += $item->_attributes;

But in DateTimeDefaultFormatter it was removed, because it doing in block above.

So in DateRangeDefaultFormatter.php there isn't that block and there isn't code for defining #attributes array.

And datetime_range-unsupported_operand_types.patch just fixes this.

mpdonadio’s picture

Status: Active » Fixed

#15, what version of Drupal are you using? This isn't in a point release yet, so you would have to be applying the patch manually or using -dev?

The relevant portion of applied-patch code is:

        if ($start_date->format('U') !== $end_date->format('U')) {
          $elements[$delta] = [
            'start_date' => $this->buildDateWithIsoAttribute($start_date),
            'separator' => ['#plain_text' => ' ' . $separator . ' '],
            'end_date' => $this->buildDateWithIsoAttribute($end_date),
          ];
        }
        else {
          $elements[$delta] = $this->buildDateWithIsoAttribute($start_date);
          if (!empty($item->_attributes)) {
            $elements[$delta]['#attributes'] += $item->_attributes;
            // Unset field item attributes since they have been included in the
            // formatter output and should not be rendered in the field template.
            unset($item->_attributes);
          }
        }

In the TRUE path of the if, we defer using the `$item->_attributes` inside the formatter itself since we won't have a wrapper; instead it will be output with the field template. This is covered in lines 146, 305, 466, and 544 of DateRangeFieldTest. In the FALSE path, the `$elements[$delta]['#attributes']` has been set already from `$this->buildDateWithIsoAttribute` (this sets the datetime attribute for the time element) and covered by lines 214 and 381 of DateRangeFieldTest. These six lines should cover the combinations of date+time, date-only, and all-date with start==and and start!=end for the default formatter.

If you are still having problems, please post a followup issue with as many details as possible (a failing test would be ideal), so we can see what we missed.

iamdroid’s picture

Sorry, I didn't noticed that current 8.2.x (8.2.1+87-dev) don't includes changes yet, and just download last build without applying patch manually.

Now all works fine. Thank You very much!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.