diff --git a/core/lib/Drupal/Core/TypedData/ComputedItemListTrait.php b/core/lib/Drupal/Core/Field/ComputedFieldItemListTrait.php similarity index 67% rename from core/lib/Drupal/Core/TypedData/ComputedItemListTrait.php rename to core/lib/Drupal/Core/Field/ComputedFieldItemListTrait.php index a9b5673..8c0d7b1 100644 --- a/core/lib/Drupal/Core/TypedData/ComputedItemListTrait.php +++ b/core/lib/Drupal/Core/Field/ComputedFieldItemListTrait.php @@ -1,16 +1,18 @@ doComputeValue(); - return parent::get($index); + + return isset($this->list[$index]) ? $this->list[$index] : NULL; } /** @@ -142,7 +156,21 @@ public function count() { public function applyDefaultValue($notify = TRUE) { // Default values do not make sense for computed fields. However, this // method can be overridden if needed. - return; + return $this; + } + + /** + * {@inheritdoc} + */ + public function filterEmptyItems() { + // If the values were never computed, for example when the field has not + // been interacted with during the life cycle of an entity, there is no need + // to compute them now. + if ($this->valueComputed === FALSE) { + return $this; + } + + return parent::filterEmptyItems(); } } diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestFieldItemList.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestFieldItemList.php index cacc6e0..e3bff76 100644 --- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestFieldItemList.php +++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/ComputedTestFieldItemList.php @@ -2,20 +2,25 @@ namespace Drupal\entity_test\Plugin\Field; +use Drupal\Core\Field\ComputedFieldItemListTrait; use Drupal\Core\Field\FieldItemList; -use Drupal\Core\TypedData\ComputedItemListTrait; /** * A computed field item list. */ class ComputedTestFieldItemList extends FieldItemList { - use ComputedItemListTrait; + use ComputedFieldItemListTrait; /** * Compute the list property from state. */ protected function computeValue() { + // Count the number of times this method has been executed during the + // lifecycle of an entity. + $execution_count = \Drupal::state()->get('computed_test_field_execution', 0); + \Drupal::state()->set('computed_test_field_execution', ++$execution_count); + foreach (\Drupal::state()->get('entity_test_computed_field_item_list_value', []) as $delta => $item) { $this->list[$delta] = $this->createItem($delta, $item); } diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php index adb04ab..8386508 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php @@ -742,14 +742,33 @@ public function testComputedProperties() { * Tests all the interaction points of a computed field. */ public function testComputedFields() { + $this->installEntitySchema('entity_test_computed_field'); + \Drupal::state()->set('entity_test_computed_field_item_list_value', ['foo computed']); + // Check that the values are not computed unnecessarily during the life + // cycle of an entity when there the field is not interacted with directly. + \Drupal::state()->set('computed_test_field_execution', 0); + $entity = EntityTestComputedField::create([]); + $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0)); + + $entity->name->value = $this->randomString(); + $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0)); + + $entity->save(); + $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0)); + // Test \Drupal\Core\TypedData\ComputedItemListTrait::getValue(). + \Drupal::state()->set('computed_test_field_execution', 0); $entity = EntityTestComputedField::create([]); $this->assertSame([['value' => 'foo computed']], $entity->computed_string_field->getValue()); + // Check that the values are only computed once. + $this->assertSame(1, \Drupal::state()->get('computed_test_field_execution', 0)); + // Test \Drupal\Core\TypedData\ComputedItemListTrait::setValue(). This also // checks that a subsequent getter does not try to re-compute the value. + \Drupal::state()->set('computed_test_field_execution', 0); $entity = EntityTestComputedField::create([]); $entity->computed_string_field->setValue([ ['value' => 'foo computed 1'], @@ -757,6 +776,10 @@ public function testComputedFields() { ]); $this->assertSame([['value' => 'foo computed 1'], ['value' => 'foo computed 2']], $entity->computed_string_field->getValue()); + // Check that the values have not been computed when they were explicitly + // set. + $this->assertSame(0, \Drupal::state()->get('computed_test_field_execution', 0)); + // Test \Drupal\Core\TypedData\ComputedItemListTrait::getString(). $entity = EntityTestComputedField::create([]); $this->assertSame('foo computed', $entity->computed_string_field->getString());