Problem/Motivation

This function currently always use ->value.

For entity reference items, this is just NULL and you would have to use ->target_id

Proposed resolution

Use \Drupal\Core\Field\FieldItemBase::mainPropertyName to fetch the right value.

Remaining tasks

User interface changes

API changes

Data model changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dawehner created an issue. See original summary.

dawehner’s picture

Assigned: Unassigned » dawehner

Working on it now

dawehner’s picture

Status: Active » Needs review
Issue tags: +Needs tests
FileSize
1.12 KB

Here is a first bugfix

Status: Needs review » Needs work

The last submitted patch, 3: 2621504-3.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
4.18 KB

There we go

mpdonadio’s picture

+++ b/core/modules/views/src/Tests/Handler/FieldFieldTest.php
@@ -67,12 +68,14 @@ class FieldFieldTest extends ViewKernelTestBase {
   protected function setUp() {
-    parent::setUp();
+    parent::setUp(FALSE);
 
-    $this->installEntitySchema('entity_test');
     $this->installEntitySchema('user');
+    $this->installEntitySchema('entity_test');
     $this->installEntitySchema('entity_test_rev');
 
+    ViewTestData::createTestViews(get_class($this), array('views_test_config'));
+
     // Bypass any field access.
     $this->adminUser = User::create(['name' => $this->randomString()]);
     $this->adminUser->save();
@@ -551,4 +554,30 @@ public function testMissingBundleFieldRender() {

@@ -551,4 +554,30 @@ public function testMissingBundleFieldRender() {
     $this->assertEqual('', $executable->getStyle()->getField(6, 'field_test'));
   }
 

I think this needs some comments as to why you are doing the view setup yourself and not using the one from ViewKernelTestBase.

dawehner’s picture

I think this needs some comments as to why you are doing the view setup yourself and not using the one from ViewKernelTestBase.

Here is one.

mpdonadio’s picture

Status: Needs review » Reviewed & tested by the community

Change looks good, and should future proof fields that are implemented in similar ways. Test looks good and valid. Verified test fails w/o the patch.

The last submitted patch, 3: 2621504-3.patch, failed testing.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -1004,12 +1004,15 @@ public function getValue(ResultRow $values, $field = NULL) {
+      $main_property_name = $field_item_list->first()->mainPropertyName();
+      return $field ? $field_item_list->$field : $field_item_list->first()->{$main_property_name};
...
+      $main_property_name = $field_item->mainPropertyName();
+      $values[] = $field ? $field_item->$field : $field_item->{$main_property_name};

We only need to get the main_property_name for one side of the ternary so lets just use an if statement instead.

Do have test coverage of when the cardinality is 1 and when it is higher?

dawehner’s picture

Assigned: dawehner » Unassigned
Status: Needs work » Needs review
FileSize
4.33 KB
973 bytes

Do have test coverage of when the cardinality is 1 and when it is higher?

Well, we probably render a node title and an image field.

\Drupal\rest\Tests\Views\StyleSerializerTest::testFieldapiField uses certainly cardinality 1

morsok’s picture

This patch helped me to get a value from a color field (see #2701933: Declare the main property name for related informations).

Looks good to me, maybe the title should change from "needs to support entity reference items" to "needs to support field which main value is not 'value'".

timmillwood’s picture

Version: 8.0.x-dev » 8.1.x-dev
mpdonadio’s picture

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

This doesn't apply against 8.1.x. Needs reroll.

mpdonadio’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
4.39 KB

Let see.

dawehner’s picture

@mpdonadio Do you think the patch looks more or less sane beside of that?

dawehner’s picture

Issue tags: +DrupalBCDays
mpdonadio’s picture

#16, I think so. You can't interdiff them b/c git output the hunks in a different order, but they all look the same. The conflict was b/c the namespace change in #2304461: KernelTestBaseTNG™ for KernelTestBase. It was an easy merge once I realized that.

dawehner’s picture

Yeah I think its totally fine for you to RTBC the patch :)

tstoeckler’s picture

Status: Needs review » Needs work

FieldItemInterface::mainPropertyName() can return NULL so would be nice to add a check for that. I don't think we need to support a property named 0 so if ($main_property_name = $field_item->mainPropertyName()) { ... } should be fine.

dawehner’s picture

Status: Needs work » Needs review
FileSize
927 bytes
4.57 KB

FieldItemInterface::mainPropertyName() can return NULL so would be nice to add a check for that. I don't think we need to support a property named 0 so if ($main_property_name = $field_item->mainPropertyName()) { ... } should be fine.

Haha, I hope the same.

Here is an update of the patch.

jibran’s picture

DER has this in DynamicEntityReferenceItem.php

  /**
   * {@inheritdoc}
   */
  public static function mainPropertyName() {
    // We have two main properties i.e. target_type and target_id.
    return NULL;
  }

So this will generate an error here.

dawehner’s picture

So this will generate an error here.

No, it won't. We now check for the main property and return NULL instead. I'm sorry, but if the field type doesn't return anything useful we cannot guess what to render. Decide on one: 'target_id' IMHO.

Lendude’s picture

Status: Needs review » Needs work
+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -1031,12 +1031,22 @@ public function getValue(ResultRow $values, $field = NULL) {
     if ($field_item_definition->getFieldStorageDefinition()->getCardinality() == 1) {
-      return $field ? $field_item_list->$field : $field_item_list->value;
+      if ($field) {
+        return $field_item_list->$field;
+      }
+      /** @var \Drupal\Core\Field\FieldItemInterface::mainPropertyName $first_item */
+      // Potentially field items might have no main property. There is nothing
+      // we can do then.
+      elseif (($first_item = $field_item_list->first()) && $main_property_name = $first_item->mainPropertyName()) {
+        return $first_item->{$main_property_name};
+      }
     }

With the current logic, if the cardinality == 1 but both the if and the elseif are false, you will fall through to the cardinality > 1 logic and return an array (possibly an empty one).
Shouldn't the cardinality == 1 logic include an else, and return something other then an array, maybe the original $field_item_list->value fallback?

dawehner’s picture

Status: Needs work » Needs review
FileSize
4.64 KB
1.88 KB

Nice point! This allowed us to refactor the code and makes it actually much nicer

mpdonadio’s picture

Does #24 and #25 mean that we have a test coverage problem? Maybe the one mentioned in #10 didn't cover this for some reason?

dawehner’s picture

Here is a better test for it.

Status: Needs review » Needs work

The last submitted patch, 27: 2621504-27.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
Issue tags: +neworleans2016
FileSize
6.2 KB
2.38 KB
jibran’s picture

but if the field type doesn't return anything useful we cannot guess what to render. Decide on one: 'target_id' IMHO.

Let me create an issue in DER and discuss it with ER maintainers as well.

Status: Needs review » Needs work

The last submitted patch, 29: 2621504-29.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review

.

Lendude’s picture

Status: Needs review » Needs work

@dawehner oh yeah this now looks way better. Also the new test addresses @alexpotts comment in #10, so nice!

+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -1030,15 +1030,29 @@ public function getValue(ResultRow $values, $field = NULL) {
+      // Potentially field items might have no main property. There is nothing
+      // we can do then.
...
+        // Try out value, which might be used, even if its not defined as main
+        // propery name.

The first comment isn't quite right anymore, there is something we can do, namely 'take a guess'. Maybe combine the two comments into one with something like:
Find the value using the main property of the field. If no main property is provided fall back to 'value'.

Otherwise, the second comment contains two grammatical errors: its => it's and propery => property

Nitpicking aside, this looks ready.

dawehner’s picture

Status: Needs work » Needs review
FileSize
6.12 KB
970 bytes

Otherwise, the second comment contains two grammatical errors: its => it's and propery => property

Good idea

Lendude’s picture

Status: Needs review » Reviewed & tested by the community

Thanks!

  • catch committed 6fb3044 on 8.2.x
    Issue #2621504 by dawehner, mpdonadio, Lendude, jibran: \Drupal\views\...

  • catch committed 4378050 on 8.1.x
    Issue #2621504 by dawehner, mpdonadio, Lendude, jibran: \Drupal\views\...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.2.x and cherry-picked to 8.1.x. Thanks!

jibran’s picture

Sorry for being late here but I have a question.

+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -1030,15 +1030,27 @@ public function getValue(ResultRow $values, $field = NULL) {
-      $values[] = $field ? $field_item->$field : $field_item->value;
+      /** @var \Drupal\Core\Field\FieldItemInterface $field_item */
+      if ($field) {
+        $values[] = $field_item->$field;
+      }
+      // Find the value using the main property of the field. If no main
+      // property is provided fall back to 'value'.
+      elseif ($main_property_name = $field_item->mainPropertyName()) {
+        $values[] = $field_item->{$main_property_name};
+      }
+      else {
+        $values[] = $field_item->value;
+      }
+    }
+    if ($field_item_definition->getFieldStorageDefinition()->getCardinality() == 1) {
+      return reset($values);
+    }
+    else {
+      return $values;

Instead of all this why not just use $field_item->getValue() here?

jibran’s picture

Status: Needs review » Needs work

The last submitted patch, 40: 2621504-1.patch, failed testing.

dawehner’s picture

Thank you @jibran
I think we could replace

+      if ($field) {
+        $values[] = $field_item->$field;
+      }
+      // Find the value using the main property of the field. If no main
+      // property is provided fall back to 'value'.
+      elseif ($main_property_name = $field_item->mainPropertyName()) {
+        $values[] = $field_item->{$main_property_name};
+      }
+      else {
+        $values[] = $field_item->value;
+      }

with

$value = $field_item->getValue();
$properties = array_filter([$field, $field_item->getMainPropertyName(), 'value']);
$values[] = $value[reset($properties)];

. Do you think this would make sense?

dawehner’s picture

Status: Needs work » Fixed

@jibran
Let's take that onto a followup!

Status: Fixed » Closed (fixed)

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