diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php index 60c36b5..56260e6 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php @@ -9,7 +9,6 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -299,8 +298,10 @@ protected function invokeFieldMethod($method, ContentEntityInterface $entity) { * Specifies whether the entity is being updated or created. */ protected function invokeFieldPostSave(ContentEntityInterface $entity, $update) { + // For each entity translation this returns an array of resave flags keyed + // by field name, thus we merge them to obtain a list of fields to resave. $resave = []; - foreach ($this->invokeFieldMethod('postSave', $entity, $update) as $langcode => $results) { + foreach ($this->invokeFieldMethod('postSave', $entity, $update) as $results) { $resave += array_filter($results); } if ($resave) { diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php index 937cb5f..927a946 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php @@ -936,10 +936,10 @@ public function save(EntityInterface $entity) { */ protected function doSaveFieldItems(ContentEntityInterface $entity, array $names = []) { if (!$names) { - $this->doRegularSave($entity); + $this->doSaveFieldItemsFull($entity); } else { - $this->doResaveFieldItems($entity, $names); + $this->doSaveFieldItemsPartial($entity, $names); } } @@ -951,7 +951,7 @@ protected function doSaveFieldItems(ContentEntityInterface $entity, array $names * * @internal */ - protected function doRegularSave(ContentEntityInterface $entity) { + protected function doSaveFieldItemsFull(ContentEntityInterface $entity) { // Create the storage record to be saved. $record = $this->mapToStorageRecord($entity); @@ -1001,7 +1001,7 @@ protected function doRegularSave(ContentEntityInterface $entity) { } /** - * Resaves a set of entity fields to the database. + * Saves a set of entity fields to the database. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity object. @@ -1010,7 +1010,7 @@ protected function doRegularSave(ContentEntityInterface $entity) { * * @internal */ - protected function doResaveFieldItems(ContentEntityInterface $entity, array $names) { + protected function doSaveFieldItemsPartial(ContentEntityInterface $entity, array $names) { $table_mapping = $this->getTableMapping(); $storage_definitions = $this->entityManager->getFieldStorageDefinitions($this->entityTypeId); $shared_table_fields = FALSE; diff --git a/core/lib/Drupal/Core/Field/FieldItemBase.php b/core/lib/Drupal/Core/Field/FieldItemBase.php index c1441bb..8e50c6a 100644 --- a/core/lib/Drupal/Core/Field/FieldItemBase.php +++ b/core/lib/Drupal/Core/Field/FieldItemBase.php @@ -201,9 +201,7 @@ public function preSave() { } /** * {@inheritdoc} */ - public function postSave($update) { - return FALSE; - } + public function postSave($update) { } /** * {@inheritdoc} diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileFieldItemList.php b/core/modules/file/src/Plugin/Field/FieldType/FileFieldItemList.php index 033a742..8b0aac1 100644 --- a/core/modules/file/src/Plugin/Field/FieldType/FileFieldItemList.php +++ b/core/modules/file/src/Plugin/Field/FieldType/FileFieldItemList.php @@ -73,8 +73,6 @@ public function postSave($update) { } } } - - return FALSE; } /** diff --git a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php index 9ad35ac..4bfe0d0 100644 --- a/core/modules/path/src/Plugin/Field/FieldType/PathItem.php +++ b/core/modules/path/src/Plugin/Field/FieldType/PathItem.php @@ -75,7 +75,6 @@ public function postSave($update) { \Drupal::service('path.alias_storage')->save($entity->urlInfo()->getInternalPath(), $this->alias, $this->getLangcode(), $this->pid); } } - return FALSE; } /** diff --git a/core/modules/system/src/Tests/Field/FieldItemTest.php b/core/modules/system/src/Tests/Field/FieldItemTest.php new file mode 100644 index 0000000..b89b706 --- /dev/null +++ b/core/modules/system/src/Tests/Field/FieldItemTest.php @@ -0,0 +1,98 @@ +fieldName = Unicode::strtolower($this->randomMachineName()); + $entity_type_id = 'entity_test'; + + /** @var \Drupal\field\Entity\FieldStorageConfig $field_storage */ + FieldStorageConfig::create([ + 'field_name' => $this->fieldName, + 'type' => 'field_test', + 'entity_type' => $entity_type_id, + 'cardinality' => 1, + ])->save(); + + FieldConfig::create([ + 'entity_type' => $entity_type_id, + 'field_name' => $this->fieldName, + 'bundle' => $entity_type_id, + 'label' => 'Test field', + ])->save(); + + $this->entityManager->clearCachedDefinitions(); + $definitions = $this->entityManager->getFieldStorageDefinitions('entity_test'); + $this->assertTrue(!empty($definitions[$this->fieldName])); + } + + /** + * Tests the field item save workflow. + */ + public function testSaveWorkflow() { + $entity = EntityTest::create([ + 'name' => $this->randomString(), + $this->fieldName => $this->randomString(), + ]); + + // Save a new entity and verify that the initial field value is overwritten + // with a value containing the entity id, which implies a resave. Check that + // the entity data structure and the stored values match. + $this->assertSavedFieldItemValue($entity, "field_test:{$this->fieldName}:1:1"); + + // Update the entity and verify that the field value is overwritten on + // presave if it is not resaved. + $this->assertSavedFieldItemValue($entity, 'overwritten'); + + // Flag the field value as needing to be resaved and verify it actually is. + $entity->{$this->fieldName}->value = 'resave'; + $this->assertSavedFieldItemValue($entity, "field_test:{$this->fieldName}:1:3"); + } + + /** + * Checks that the saved field item value matches the expected one. + * + * @param \Drupal\entity_test\Entity\EntityTest $entity + * The test entity. + * @param $expected_value + * The expected field item value. + * + * @return bool + * TRUE if the item value matches expectations, FALSE otherwise. + */ + protected function assertSavedFieldItemValue(EntityTest $entity, $expected_value) { + $entity->save(); + $result = $this->assertEqual($entity->{$this->fieldName}->value, $expected_value); + $entity = $this->reloadEntity($entity); + return $result && $this->assertEqual($entity->{$this->fieldName}->value, $expected_value); + } + +} diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php new file mode 100644 index 0000000..9e8dc74 --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php @@ -0,0 +1,105 @@ +setLabel(new TranslationWrapper('Test value')) + ->setRequired(TRUE); + + return $properties; + } + + /** + * {@inheritdoc} + */ + public static function schema(FieldStorageDefinitionInterface $field_definition) { + return array( + 'columns' => array( + 'value' => array( + 'type' => 'varchar', + 'length' => 255, + ), + ), + ); + } + + /** + * {@inheritdoc} + */ + public function preSave() { + static::$counter++; + + // Overwrite the field value unless it is going to be overridden, in which + // case its final value will already be different from the current one. + if (!$this->getEntity()->isNew() && !$this->mustResave()) { + $this->setValue('overwritten'); + } + } + + /** + * {@inheritdoc} + */ + public function postSave($update) { + // Determine whether the field value should be rewritten to the storage. We + // always rewrite on create as we need to store a value including the entity + // id. + $resave = !$update || $this->mustResave(); + + if ($resave) { + $entity = $this->getEntity(); + $definition = $this->getFieldDefinition(); + $field_name = $definition->getName(); + $value = 'field_test:' . $field_name . ':' . $entity->id() . ':' . static::$counter; + $this->setValue($value); + } + + return $resave; + } + + /** + * Checks whether the field item value should be resaved. + * + * @return bool + * TRUE if the item should be resaved, FALSE otherwise. + */ + protected function mustResave() { + return $this->getValue()['value'] == 'resave'; + } + +}