diff --git a/core/lib/Drupal/Core/Field/FieldItemListComputedInterface.php b/core/lib/Drupal/Core/Field/FieldItemListComputedInterface.php new file mode 100644 index 0000000000..e3f794fb08 --- /dev/null +++ b/core/lib/Drupal/Core/Field/FieldItemListComputedInterface.php @@ -0,0 +1,17 @@ +list[0]) && $this->definition->isComputed()) { - $this->list[0] = $this->createItem(0); - } + return isset($this->list[$index]) ? $this->list[$index] : NULL; } diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php index dbf0d5fec5..d54d637e58 100644 --- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php +++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php @@ -7,6 +7,7 @@ use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Field\FieldItemListComputedInterface; use Drupal\Core\Plugin\DefaultPluginManager; use Drupal\Core\TypedData\Validation\ExecutionContextFactory; use Drupal\Core\TypedData\Validation\RecursiveValidator; @@ -200,6 +201,11 @@ public function getPropertyInstance(TypedDataInterface $object, $property_name, if (isset($value)) { $property->setValue($value, FALSE); } + elseif ($property instanceof FieldItemListComputedInterface) { + // populate the computed list with values as there are no initial values + // to set. + $property->computeValue(); + } return $property; } diff --git a/core/modules/field/tests/modules/field_computed_test/field_computed_test.info.yml b/core/modules/field/tests/modules/field_computed_test/field_computed_test.info.yml new file mode 100644 index 0000000000..42f6216f07 --- /dev/null +++ b/core/modules/field/tests/modules/field_computed_test/field_computed_test.info.yml @@ -0,0 +1,6 @@ +name: 'Field Computed Test' +type: module +description: 'Support module for the computed field tests.' +core: 8.x +package: Testing +version: VERSION diff --git a/core/modules/field/tests/modules/field_computed_test/field_computed_test.module b/core/modules/field/tests/modules/field_computed_test/field_computed_test.module new file mode 100644 index 0000000000..28bd677665 --- /dev/null +++ b/core/modules/field/tests/modules/field_computed_test/field_computed_test.module @@ -0,0 +1,60 @@ +id() === 'node') { + $fields = array(); + + $fields['field_dice_count'] = BaseFieldDefinition::create('integer') + ->setCustomStorage(false) + ->setLabel(t('Dice count')) + ->setDisplayOptions('view', array( + 'label' => 'inline', + 'type' => 'text_textfield', + 'weight' => 0, + )) + ->setDisplayOptions('form', array( + 'type' => 'integer', + 'weight' => 10, + )) + ->setDisplayConfigurable('form', TRUE); + + // set a custom list class an explicitly define the field to be computed + $fields['field_dice_result'] = BaseFieldDefinition::create('integer') + ->setLabel(t('Dice result')) + ->setClass('\Drupal\field_computed_test\Plugin\Field\FieldType\DiceItemList') + ->setComputed(TRUE) + ->setDisplayOptions('view', array( + 'label' => 'inline', + 'type' => 'number_integer', + 'weight' => 0, + )); + + // set a custom list class and rely on that class to mark the field as computed + $fields['field_dice_result_v2'] = BaseFieldDefinition::create('integer') + ->setLabel(t('Dice result V2')) + ->setClass('\Drupal\field_computed_test\Plugin\Field\FieldType\DiceItemList') + ->setDisplayOptions('view', array( + 'label' => 'inline', + 'type' => 'number_integer', + 'weight' => 0, + )); + + // use a field type which uses a list class that marks the field as computed + $fields['field_dice_result_v3'] = BaseFieldDefinition::create('dice') + ->setLabel(t('Dice result V3')) + ->setDisplayOptions('view', array( + 'label' => 'inline', + 'weight' => 0, + )) + ->setDisplayOptions('form', array( + 'type' => 'integer', + 'weight' => 10, + )); + + return $fields; + } + +} diff --git a/core/modules/field/tests/modules/field_computed_test/src/Plugin/Field/FieldType/DiceItem.php b/core/modules/field/tests/modules/field_computed_test/src/Plugin/Field/FieldType/DiceItem.php new file mode 100644 index 0000000000..cb99035727 --- /dev/null +++ b/core/modules/field/tests/modules/field_computed_test/src/Plugin/Field/FieldType/DiceItem.php @@ -0,0 +1,39 @@ +setLabel(t('Integer value')); + return $properties; + } + + /** + * {@inheritdoc} + */ + public static function schema(FieldStorageDefinitionInterface $field_definition) { + return [ + 'columns' => [], + ]; + } + +} diff --git a/core/modules/field/tests/modules/field_computed_test/src/Plugin/Field/FieldType/DiceItemList.php b/core/modules/field/tests/modules/field_computed_test/src/Plugin/Field/FieldType/DiceItemList.php new file mode 100644 index 0000000000..b671f9ccb5 --- /dev/null +++ b/core/modules/field/tests/modules/field_computed_test/src/Plugin/Field/FieldType/DiceItemList.php @@ -0,0 +1,38 @@ +computeItemValues(); + $this->setValue($values); + } + + /** + * This should compute the values that are used to create the field items. + * + * @return array + * The values for the field items. + */ + protected function computeItemValues() { + $items_count = $this->getEntity()->field_dice_count->value; + $values = []; + for ($delta = 0; $delta < $items_count; $delta++) { + $values[$delta] = [ + 'value' => rand(1, 6), + ]; + } + return $values; + } + +} diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php index 2b929f3608..1f4bfa48fc 100644 --- a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php @@ -83,6 +83,13 @@ protected function setUp() { ->method('getDefaultFieldSettings') ->willReturn([]); + $typed_data_manager = $this->getMock('Drupal\Component\Plugin\PluginManagerInterface'); + // @todo: maybe use a reasonable argument + $typed_data_manager->expects($this->any()) + ->method('getDefinition') + ->with($this->anything()) + ->will($this->returnValue(['list_class' => '\Drupal\Core\Field\FieldItemList'])); + $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $this->renderer = $this->getMock('Drupal\Core\Render\RendererInterface'); @@ -94,6 +101,7 @@ protected function setUp() { $this->container = new ContainerBuilder(); $this->container->set('plugin.manager.field.field_type', $this->fieldTypePluginManager); + $this->container->set('typed_data_manager', $typed_data_manager); \Drupal::setContainer($this->container); } diff --git a/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php b/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php index 341ed9cf8c..a7918fb9b7 100644 --- a/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php @@ -64,8 +64,16 @@ protected function setUp() { ->with($this->fieldType) ->will($this->returnValue($this->fieldTypeDefinition['field_settings'])); + $typed_data_manager = $this->getMock('Drupal\Component\Plugin\PluginManagerInterface'); + // @todo: maybe use a reasonable argument + $typed_data_manager->expects($this->any()) + ->method('getDefinition') + ->with($this->anything()) + ->will($this->returnValue(['list_class' => '\Drupal\Core\Field\FieldItemList'])); + $container = new ContainerBuilder(); $container->set('plugin.manager.field.field_type', $field_type_manager); + $container->set('typed_data_manager', $typed_data_manager); \Drupal::setContainer($container); } diff --git a/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php index 953daac887..d5832d7fbd 100644 --- a/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php @@ -141,8 +141,15 @@ protected function setUp() { $this->typedDataManager = $this->getMock(TypedDataManagerInterface::class); $this->typedDataManager->expects($this->any()) ->method('getDefinition') - ->with('entity') - ->will($this->returnValue(['class' => '\Drupal\Core\Entity\Plugin\DataType\EntityAdapter'])); + ->will($this->returnCallback(function($plugin_id){ + switch ($plugin_id) { + case 'entity': + return ['class' => '\Drupal\Core\Entity\Plugin\DataType\EntityAdapter']; + default: + // @todo: maybe use a reasonable argument value + return ['list_class' => '\Drupal\Core\Field\FieldItemList']; + } + })); $this->typedDataManager->expects($this->any()) ->method('getDefaultConstraints') ->willReturn([]);