diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php index 3834dae..18b8607 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php @@ -100,16 +100,16 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora * The entity type definition. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory service. - * @param \Drupal\Component\Uuid\UuidInterface $uuid_service + * @param \Drupal\Component\Uuid\UuidInterface $uuid_generator * The UUID service. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. */ - public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager) { + public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_generator, LanguageManagerInterface $language_manager) { parent::__construct($entity_type); $this->configFactory = $config_factory; - $this->uuidService = $uuid_service; + $this->uuidGenerator = $uuid_generator; $this->languageManager = $language_manager; } @@ -446,8 +446,8 @@ public function createFromStorageRecord(array $values) { */ protected function _doCreateFromStorageRecord(array $values, $is_syncing = FALSE) { // Assign a new UUID if there is none yet. - if ($this->uuidKey && $this->uuidService && !isset($values[$this->uuidKey])) { - $values[$this->uuidKey] = $this->uuidService->generate(); + if ($this->uuidKey && $this->uuidGenerator && !isset($values[$this->uuidKey])) { + $values[$this->uuidKey] = $this->uuidGenerator->generate(); } $data = $this->mapFromStorageRecords(array($values)); $entity = current($data); diff --git a/core/lib/Drupal/Core/Database/Schema.php b/core/lib/Drupal/Core/Database/Schema.php index 8b9eb7e..c4bba41 100644 --- a/core/lib/Drupal/Core/Database/Schema.php +++ b/core/lib/Drupal/Core/Database/Schema.php @@ -305,7 +305,7 @@ public function fieldExists($table, $column) { * created field will be set to the value of the key in all rows. * This is most useful for creating NOT NULL columns with no default * value in existing tables. - * Alternatively, the 'initial_form_field' key may be used, which will + * Alternatively, the 'initial_from_field' key may be used, which will * auto-populate the new field with values from the specified field. * @param $keys_new * (optional) Keys and indexes specification to be created on the diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index b5fccbd..3494fb3 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -335,6 +335,13 @@ public function getRevisionId() { /** * {@inheritdoc} */ + public function getRevisionUuid() { + return $this->getEntityKey('revision_uuid'); + } + + /** + * {@inheritdoc} + */ public function isTranslatable() { // Check that the bundle is translatable, the entity has a language defined // and if we have more than one language on the site. @@ -996,6 +1003,12 @@ public function createDuplicate() { // Check whether the entity type supports revisions and initialize it if so. if ($entity_type->isRevisionable()) { $duplicate->{$entity_type->getKey('revision')}->value = NULL; + + // Check if the entity type supports revision UUIDs and generate a new one + // if so. + if ($entity_type->hasKey('revision_uuid')) { + $duplicate->{$entity_type->getKey('revision_uuid')}->value = $this->uuidGenerator()->generate(); + } } return $duplicate; @@ -1133,6 +1146,11 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setReadOnly(TRUE) ->setSetting('unsigned', TRUE); } + if ($entity_type->hasKey('revision_uuid')) { + $fields[$entity_type->getKey('revision_uuid')] = BaseFieldDefinition::create('uuid') + ->setLabel(new TranslatableMarkup('Revision UUID')) + ->setReadOnly(TRUE); + } if ($entity_type->hasKey('langcode')) { $fields[$entity_type->getKey('langcode')] = BaseFieldDefinition::create('language') ->setLabel(new TranslatableMarkup('Language')) diff --git a/core/lib/Drupal/Core/Entity/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php index f583121..99ca3fc 100644 --- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php @@ -63,7 +63,7 @@ * * @var \Drupal\Component\Uuid\UuidInterface */ - protected $uuidService; + protected $uuidGenerator; /** * Name of the entity class. @@ -177,8 +177,8 @@ public function create(array $values = array()) { $entity_class::preCreate($this, $values); // Assign a new UUID if there is none yet. - if ($this->uuidKey && $this->uuidService && !isset($values[$this->uuidKey])) { - $values[$this->uuidKey] = $this->uuidService->generate(); + if ($this->uuidKey && $this->uuidGenerator && !isset($values[$this->uuidKey])) { + $values[$this->uuidKey] = $this->uuidGenerator->generate(); } $entity = $this->doCreate($values); diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php index a63415b..a1fbd28 100644 --- a/core/lib/Drupal/Core/Entity/EntityType.php +++ b/core/lib/Drupal/Core/Entity/EntityType.php @@ -288,10 +288,18 @@ public function __construct($definition) { // Ensure defaults. $this->entity_keys += array( 'revision' => '', + 'revision_uuid' => '', 'bundle' => '', 'langcode' => '', 'default_langcode' => 'default_langcode', ); + + // If the entity type is revisionable, provide a default value for the + // 'revision_uuid' key. + if (!empty($this->entity_keys['revision']) && empty($this->entity_keys['revision_uuid'])) { + $this->entity_keys['revision_uuid'] = 'revision_uuid'; + } + $this->handlers += array( 'access' => 'Drupal\Core\Entity\EntityAccessControlHandler', ); diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php index 398347f..1a67f9b 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php @@ -88,6 +88,9 @@ public function getOriginalClass(); * revision ID of the entity. The Field API assumes that all revision IDs * are unique across all entities of a type. If this entry is omitted * the entities of this type are not revisionable. + * - revision_uuid: (optional) The name of the property that contains the + * revision UUID of the entity. Defaults to "revision_uuid" if the + * 'revision' key is set. * - bundle: (optional) The name of the property that contains the bundle * name for the entity. The bundle name defines which set of fields are * attached to the entity (e.g. what nodes call "content type"). This diff --git a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php index 478bca7..252c254 100644 --- a/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/KeyValueStore/KeyValueEntityStorage.php @@ -40,7 +40,7 @@ class KeyValueEntityStorage extends EntityStorageBase { * * @var \Drupal\Component\Uuid\UuidInterface */ - protected $uuidService; + protected $uuidGenerator; /** * The language manager. @@ -56,15 +56,15 @@ class KeyValueEntityStorage extends EntityStorageBase { * The entity type. * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $key_value_store * The key value store. - * @param \Drupal\Component\Uuid\UuidInterface $uuid_service + * @param \Drupal\Component\Uuid\UuidInterface $uuid_generator * The UUID service. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. */ - public function __construct(EntityTypeInterface $entity_type, KeyValueStoreInterface $key_value_store, UuidInterface $uuid_service, LanguageManagerInterface $language_manager) { + public function __construct(EntityTypeInterface $entity_type, KeyValueStoreInterface $key_value_store, UuidInterface $uuid_generator, LanguageManagerInterface $language_manager) { parent::__construct($entity_type); $this->keyValueStore = $key_value_store; - $this->uuidService = $uuid_service; + $this->uuidGenerator = $uuid_generator; $this->languageManager = $language_manager; // Check if the entity type supports UUIDs. diff --git a/core/lib/Drupal/Core/Entity/RevisionableInterface.php b/core/lib/Drupal/Core/Entity/RevisionableInterface.php index 14690e3..34b29e0 100644 --- a/core/lib/Drupal/Core/Entity/RevisionableInterface.php +++ b/core/lib/Drupal/Core/Entity/RevisionableInterface.php @@ -40,6 +40,21 @@ public function setNewRevision($value = TRUE); public function getRevisionId(); /** + * Gets the revision universally-unique identifier of the entity. + * + * This identifier can be used when moving specific revisions between two + * systems. The UUID of the entity itself is not unique enough for the + * revision and the revision ID has no meaning. + * + * Note, all translations share the same revision UUID. + * + * @return string + * The revision UUID of the entity, or NULL if the entity does not + * support revision UUID's. + */ + public function getRevisionUuid(); + + /** * Checks if this entity is the default revision. * * @param bool $new_value diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php index 391e3444..2291a79 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Entity\Sql; +use Drupal\Component\Uuid\UuidInterface; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Database\Connection; use Drupal\Core\Database\Database; @@ -55,6 +56,15 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt protected $revisionKey = FALSE; /** + * Name of entity's revision UUID database field, if it supports revisions. + * + * Has the value FALSE if this entity does not use revisions. + * + * @var string + */ + protected $revisionUuidKey = FALSE; + + /** * The entity langcode key. * * @var string|bool @@ -126,7 +136,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI $container->get('database'), $container->get('entity.manager'), $container->get('cache.entity'), - $container->get('language_manager') + $container->get('language_manager'), + $container->get('uuid') ); } @@ -154,11 +165,14 @@ public function getFieldStorageDefinitions() { * The cache backend to be used. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. + * @param \Drupal\Component\Uuid\UuidInterface $uuid_service + * The UUID service. */ - public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager) { + public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, UuidInterface $uuid_service) { parent::__construct($entity_type, $entity_manager, $cache); $this->database = $database; $this->languageManager = $language_manager; + $this->uuidGenerator = $uuid_service; $this->initTableLayout(); } @@ -170,6 +184,7 @@ protected function initTableLayout() { // are correctly reflected in the table layout. $this->tableMapping = NULL; $this->revisionKey = NULL; + $this->revisionUuidKey = NULL; $this->revisionTable = NULL; $this->dataTable = NULL; $this->revisionDataTable = NULL; @@ -180,6 +195,7 @@ protected function initTableLayout() { $revisionable = $this->entityType->isRevisionable(); if ($revisionable) { $this->revisionKey = $this->entityType->getKey('revision') ?: 'revision_id'; + $this->revisionUuidKey = $this->entityType->getKey('revision_uuid') ?: 'revision_uuid'; $this->revisionTable = $this->entityType->getRevisionTable() ?: $this->entityTypeId . '_revision'; } $translatable = $this->entityType->isTranslatable(); @@ -286,7 +302,7 @@ public function getTableMapping(array $storage_definitions = NULL) { return $table_mapping->allowsSharedTableStorage($definition); }); - $key_fields = array_values(array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey))); + $key_fields = array_values(array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->revisionUuidKey, $this->langcodeKey))); $all_fields = array_keys($shared_table_definitions); $revisionable_fields = array_keys(array_filter($shared_table_definitions, function (FieldStorageDefinitionInterface $definition) { return $definition->isRevisionable(); @@ -316,7 +332,7 @@ public function getTableMapping(array $storage_definitions = NULL) { // denormalized in the base table but also stored in the revision table // together with the entity ID and the revision ID as identifiers. $table_mapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields)); - $revision_key_fields = array($this->idKey, $this->revisionKey); + $revision_key_fields = array($this->idKey, $this->revisionKey, $this->revisionUuidKey); $table_mapping->setFieldNames($this->revisionTable, array_merge($revision_key_fields, $revisionable_fields)); } elseif (!$revisionable && $translatable) { @@ -343,10 +359,10 @@ public function getTableMapping(array $storage_definitions = NULL) { // Like in the multilingual, non-revisionable case the UUID is not // in the data table. Additionally, do not store revision metadata // fields in the data table. - $data_fields = array_values(array_diff($all_fields, array($this->uuidKey), $revision_metadata_fields)); + $data_fields = array_values(array_diff($all_fields, array($this->uuidKey, $this->revisionUuidKey), $revision_metadata_fields)); $table_mapping->setFieldNames($this->dataTable, $data_fields); - $revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields); + $revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->revisionUuidKey, $this->langcodeKey), $revision_metadata_fields); $table_mapping->setFieldNames($this->revisionTable, $revision_base_fields); $revision_data_key_fields = array($this->idKey, $this->revisionKey, $this->langcodeKey); @@ -1022,6 +1038,11 @@ protected function saveRevision(ContentEntityInterface $entity) { $entity->preSaveRevision($this, $record); if ($entity->isNewRevision()) { + // Generating a new revision should generate a new revision UUID. + if ($this->revisionUuidKey) { + $record->{$this->revisionUuidKey} = $this->uuidGenerator->generate(); + } + $insert_id = $this->database ->insert($this->revisionTable, array('return' => Database::RETURN_INSERT_ID)) ->fields((array) $record) @@ -1033,7 +1054,10 @@ protected function saveRevision(ContentEntityInterface $entity) { } if ($entity->isDefaultRevision()) { $this->database->update($this->entityType->getBaseTable()) - ->fields(array($this->revisionKey => $record->{$this->revisionKey})) + ->fields([ + $this->revisionKey => $record->{$this->revisionKey}, + $this->revisionUuidKey => $record->{$this->revisionUuidKey}, + ]) ->condition($this->idKey, $record->{$this->idKey}) ->execute(); } @@ -1046,8 +1070,11 @@ protected function saveRevision(ContentEntityInterface $entity) { ->execute(); } - // Make sure to update the new revision key for the entity. + // Make sure to update the new revision ID and UUID keys for the entity. $entity->{$this->revisionKey}->value = $record->{$this->revisionKey}; + if ($this->revisionUuidKey) { + $entity->{$this->revisionUuidKey}->value = $record->{$this->revisionUuidKey}; + } return $record->{$this->revisionKey}; } diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php index ec3ebaf..be85cd7 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php @@ -176,7 +176,6 @@ public function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterfac if ( $storage_definition->hasCustomStorage() != $original->hasCustomStorage() || - $storage_definition->getSchema() != $original->getSchema() || $storage_definition->isRevisionable() != $original->isRevisionable() || $table_mapping->allowsSharedTableStorage($storage_definition) != $table_mapping->allowsSharedTableStorage($original) || $table_mapping->requiresDedicatedTableStorage($storage_definition) != $table_mapping->requiresDedicatedTableStorage($original) @@ -192,7 +191,40 @@ public function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterfac return FALSE; } - return $this->getSchemaFromStorageDefinition($storage_definition) != $this->loadFieldSchemaData($original); + $storage_definition_schema = $this->getSchemaFromStorageDefinition($storage_definition); + $original_schema = $this->loadFieldSchemaData($original); + + // Filter out irrelevant schema. + return $this->removeIrrelevantSchemaKeys($storage_definition_schema) != $this->removeIrrelevantSchemaKeys($original_schema); + } + + /** + * Remove irrelevant schema keys. + * + * We need to remove irrelevant schema keys before comparing two schemas to + * see if they've changed as there are certain keys that can safely be added + * at any point to existing schema. E.g. initial or initial_from_field. + * + * @param array $schema + * The schema array. + * @param array $remove_keys + * (optional) An array of keys to remove regardless of the depth. + * + * @return array + * The schema with any irrelevant keys removed. + */ + protected function removeIrrelevantSchemaKeys(&$schema, $remove_keys = ['initial', 'initial_from_field']) { + foreach ($schema as $key => &$value) { + if (is_array($value)) { + $this->removeIrrelevantSchemaKeys($value, $remove_keys); + } + else { + if (in_array($key, $remove_keys, TRUE)) { + unset($schema[$key]); + } + } + } + return $schema; } /** @@ -1153,7 +1185,9 @@ protected function createSharedTableSchema(FieldStorageDefinitionInterface $stor } if (!empty($schema[$table_name]['unique keys'])) { foreach ($schema[$table_name]['unique keys'] as $name => $specifier) { - $schema_handler->addUniqueKey($table_name, $name, $specifier); + // Check if the unique key exists because it might already have + // been created as part of the earlier entity type update event. + $this->addUniqueKey($table_name, $name, $specifier); } } } @@ -1944,7 +1978,19 @@ protected function isTableEmpty($table_name) { * Returns TRUE if there are schema changes in the column definitions. */ protected function hasColumnChanges(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) { - if ($storage_definition->getColumns() != $original->getColumns()) { + // Remove any fields that can be changed in the column array structure such + // as initial. + $new_columns = $storage_definition->getColumns(); + $original_columns = $original->getColumns(); + foreach ($new_columns as $delta => $column) { + foreach ($column as $key => $value) { + if (in_array($key, ['initial', 'initial_from_field'], TRUE)) { + unset($new_columns[$delta][$key]); + unset($original_columns[$delta][$key]); + } + } + } + if ($new_columns != $original_columns) { // Base field definitions have schema data stored in the original // definition. return TRUE; diff --git a/core/lib/Drupal/Core/Field/BaseFieldOverrideStorage.php b/core/lib/Drupal/Core/Field/BaseFieldOverrideStorage.php index 744e585..be2347d 100644 --- a/core/lib/Drupal/Core/Field/BaseFieldOverrideStorage.php +++ b/core/lib/Drupal/Core/Field/BaseFieldOverrideStorage.php @@ -20,15 +20,15 @@ class BaseFieldOverrideStorage extends FieldConfigStorageBase { * The entity type definition. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory service. - * @param \Drupal\Component\Uuid\UuidInterface $uuid_service + * @param \Drupal\Component\Uuid\UuidInterface $uuid_generator * The UUID service. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager * The field type plugin manager. */ - public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, FieldTypePluginManagerInterface $field_type_manager) { - parent::__construct($entity_type, $config_factory, $uuid_service, $language_manager); + public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_generator, LanguageManagerInterface $language_manager, FieldTypePluginManagerInterface $field_type_manager) { + parent::__construct($entity_type, $config_factory, $uuid_generator, $language_manager); $this->fieldTypeManager = $field_type_manager; } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php index 94aa8aa..b7daa16 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php @@ -45,6 +45,7 @@ public function applyDefaultValue($notify = TRUE) { public static function schema(FieldStorageDefinitionInterface $field_definition) { $schema = parent::schema($field_definition); $schema['unique keys']['value'] = array('value'); + $schema['columns']['value']['initial_from_field'] = 'UUID()'; return $schema; } diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php index 9e0531e..1da567d 100644 --- a/core/modules/block_content/src/Entity/BlockContent.php +++ b/core/modules/block_content/src/Entity/BlockContent.php @@ -160,6 +160,11 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['type']->setLabel(t('Block type')) ->setDescription(t('The block type.')); + $fields['revision_uuid'] = BaseFieldDefinition::create('uuid') + ->setLabel(t('Revision UUID')) + ->setDescription(t('The revision UUID.')) + ->setReadOnly(TRUE); + $fields['info'] = BaseFieldDefinition::create('string') ->setLabel(t('Block description')) ->setDescription(t('A brief description of your block.')) diff --git a/core/modules/comment/src/CommentStorage.php b/core/modules/comment/src/CommentStorage.php index cc4fdcf..0931f41 100644 --- a/core/modules/comment/src/CommentStorage.php +++ b/core/modules/comment/src/CommentStorage.php @@ -2,6 +2,7 @@ namespace Drupal\comment; +use Drupal\Component\Uuid\UuidInterface; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityManagerInterface; @@ -43,9 +44,11 @@ class CommentStorage extends SqlContentEntityStorage implements CommentStorageIn * Cache backend instance to use. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. + * @param \Drupal\Component\Uuid\UuidInterface $uuid_service + * The UUID service. */ - public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, AccountInterface $current_user, CacheBackendInterface $cache, LanguageManagerInterface $language_manager) { - parent::__construct($entity_info, $database, $entity_manager, $cache, $language_manager); + public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, AccountInterface $current_user, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, UuidInterface $uuid_service) { + parent::__construct($entity_info, $database, $entity_manager, $cache, $language_manager, $uuid_service); $this->currentUser = $current_user; } @@ -59,7 +62,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI $container->get('entity.manager'), $container->get('current_user'), $container->get('cache.entity'), - $container->get('language_manager') + $container->get('language_manager'), + $container->get('uuid') ); } diff --git a/core/modules/field/src/FieldConfigStorage.php b/core/modules/field/src/FieldConfigStorage.php index 0870cd4..f503de1 100644 --- a/core/modules/field/src/FieldConfigStorage.php +++ b/core/modules/field/src/FieldConfigStorage.php @@ -46,7 +46,7 @@ class FieldConfigStorage extends FieldConfigStorageBase { * The entity type definition. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory service. - * @param \Drupal\Component\Uuid\UuidInterface $uuid_service + * @param \Drupal\Component\Uuid\UuidInterface $uuid_generator * The UUID service. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. @@ -57,8 +57,8 @@ class FieldConfigStorage extends FieldConfigStorageBase { * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager * The field type plugin manager. */ - public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, StateInterface $state, FieldTypePluginManagerInterface $field_type_manager) { - parent::__construct($entity_type, $config_factory, $uuid_service, $language_manager); + public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_generator, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, StateInterface $state, FieldTypePluginManagerInterface $field_type_manager) { + parent::__construct($entity_type, $config_factory, $uuid_generator, $language_manager); $this->entityManager = $entity_manager; $this->state = $state; $this->fieldTypeManager = $field_type_manager; diff --git a/core/modules/field/src/FieldStorageConfigStorage.php b/core/modules/field/src/FieldStorageConfigStorage.php index 072b6fa..d923e20 100644 --- a/core/modules/field/src/FieldStorageConfigStorage.php +++ b/core/modules/field/src/FieldStorageConfigStorage.php @@ -54,7 +54,7 @@ class FieldStorageConfigStorage extends ConfigEntityStorage { * The entity type definition. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory service. - * @param \Drupal\Component\Uuid\UuidInterface $uuid_service + * @param \Drupal\Component\Uuid\UuidInterface $uuid_generator * The UUID service. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. @@ -67,8 +67,8 @@ class FieldStorageConfigStorage extends ConfigEntityStorage { * @param \Drupal\Component\Plugin\PluginManagerInterface\FieldTypePluginManagerInterface * The field type plugin manager. */ - public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, StateInterface $state, FieldTypePluginManagerInterface $field_type_manager) { - parent::__construct($entity_type, $config_factory, $uuid_service, $language_manager); + public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_generator, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, StateInterface $state, FieldTypePluginManagerInterface $field_type_manager) { + parent::__construct($entity_type, $config_factory, $uuid_generator, $language_manager); $this->entityManager = $entity_manager; $this->moduleHandler = $module_handler; $this->state = $state; diff --git a/core/modules/serialization/src/Tests/EntitySerializationTest.php b/core/modules/serialization/src/Tests/EntitySerializationTest.php index 00ea14f..c9e288c 100644 --- a/core/modules/serialization/src/Tests/EntitySerializationTest.php +++ b/core/modules/serialization/src/Tests/EntitySerializationTest.php @@ -119,6 +119,9 @@ public function testNormalize() { 'revision_id' => array( array('value' => 1), ), + 'revision_uuid' => array( + array('value' => $this->entity->getRevisionUuid()), + ), 'default_langcode' => array( array('value' => TRUE), ), @@ -190,6 +193,7 @@ public function testSerialize() { 'created' => '' . $this->entity->created->value . '', 'user_id' => '' . $this->user->id() . '' . $this->user->getEntityTypeId() . '' . $this->user->uuid() . '' . $this->user->url() . '', 'revision_id' => '' . $this->entity->getRevisionId() . '', + 'revision_uuid' => '' . $this->entity->getRevisionUuid() . '', 'default_langcode' => '1', 'non_rev_field' => '', 'field_test_text' => '' . $this->values['field_test_text']['value'] . '' . $this->values['field_test_text']['format'] . '', diff --git a/core/modules/shortcut/src/ShortcutSetStorage.php b/core/modules/shortcut/src/ShortcutSetStorage.php index d76c0c4..3ba6c09 100644 --- a/core/modules/shortcut/src/ShortcutSetStorage.php +++ b/core/modules/shortcut/src/ShortcutSetStorage.php @@ -30,15 +30,15 @@ class ShortcutSetStorage extends ConfigEntityStorage implements ShortcutSetStora * The entity info for the entity type. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory service. - * @param \Drupal\Component\Uuid\UuidInterface $uuid_service + * @param \Drupal\Component\Uuid\UuidInterface $uuid_generator * The UUID service. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. */ - public function __construct(EntityTypeInterface $entity_info, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) { - parent::__construct($entity_info, $config_factory, $uuid_service, $language_manager); + public function __construct(EntityTypeInterface $entity_info, ConfigFactoryInterface $config_factory, UuidInterface $uuid_generator, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) { + parent::__construct($entity_info, $config_factory, $uuid_generator, $language_manager); $this->moduleHandler = $module_handler; } diff --git a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php index 0123597..0dc7053 100644 --- a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php +++ b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php @@ -36,6 +36,7 @@ protected function updateEntityTypeToRevisionable() { $keys = $entity_type->getKeys(); $keys['revision'] = 'revision_id'; + $keys['revision_uuid'] = 'revision_uuid'; $entity_type->set('entity_keys', $keys); $this->state->set('entity_test_update.entity_type', $entity_type); diff --git a/core/modules/system/system.install b/core/modules/system/system.install index b3a0ec1..d9df274 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1647,3 +1647,25 @@ function system_update_8014() { /** * @} End of "addtogroup updates-8.0.0-rc". */ + +/** + * Install new revision_uuid schema. + */ +function system_update_8018() { + foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_id => $entity_type) { + /** @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface $manager */ + $manager = \Drupal::entityDefinitionUpdateManager(); + + // Install the new revision_uuid field. + if ($entity_type->hasKey('revision_uuid')) { + $field_manager = \Drupal::service('entity_field.manager'); + $field_storage_definitions = $field_manager->getFieldStorageDefinitions($entity_type_id); + $manager->installFieldStorageDefinition($entity_type->getKey('revision_uuid'), $entity_type_id, $entity_type->getProvider(), $field_storage_definitions[$entity_type->getKey('revision_uuid')]); + } + + // Update the existing uuid field which has a new schema. + if ($entity_type->hasKey('uuid') && $definition = $manager->getFieldStorageDefinition('uuid', $entity_type_id)) { + $manager->updateFieldStorageDefinition($definition); + } + } +} diff --git a/core/modules/system/tests/modules/entity_schema_test/entity_schema_test.module b/core/modules/system/tests/modules/entity_schema_test/entity_schema_test.module index 45892a8..ecd9da6 100644 --- a/core/modules/system/tests/modules/entity_schema_test/entity_schema_test.module +++ b/core/modules/system/tests/modules/entity_schema_test/entity_schema_test.module @@ -20,6 +20,7 @@ function entity_schema_test_entity_type_alter(array &$entity_types) { $entity_type->set('data_table', 'entity_test_field_data'); $keys = $entity_type->getKeys(); $keys['revision'] = 'revision_id'; + $keys['revision_uuid'] = 'revision_uuid'; $entity_type->set('entity_keys', $keys); } } diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php index 23cf034..8b6b7e4 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php @@ -102,6 +102,7 @@ public function testEntityTypeUpdateWithoutData() { $expected = array( 'entity_test_update' => array( t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]), + t('The %field_name field needs to be installed.', ['%field_name' => $this->entityManager->getFieldStorageDefinitions('entity_test_update')['revision_uuid']->getLabel()]), ), ); $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected); //, 'EntityDefinitionUpdateManager reports the expected change summary.'); diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php index 63ac58d..0e5fe1b 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php @@ -570,6 +570,9 @@ protected function doTestDataStructureInterfaces($entity_type) { // Field format. NULL, ); + if ($uuid = $entity->getRevisionUuid()) { + $target_strings[] = $uuid; + } asort($strings); asort($target_strings); $this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', array('%entity_type' => $entity_type))); diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityUUIDTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityUUIDTest.php index 5b0a643..4a038e1 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityUUIDTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityUUIDTest.php @@ -93,6 +93,10 @@ protected function assertCRUD($entity_type) { $this->assertNotEqual($entity_duplicate->getRevisionId(), $entity->getRevisionId()); $this->assertNotEqual($entity_duplicate->{$property}->getValue(), $entity->{$property}->getValue()); break; + case 'revision_uuid': + $this->assertNotNull($entity->getRevisionUuid()); + $this->assertNotEqual($entity_duplicate->getRevisionUuid(), $entity->getRevisionUuid()); + break; default: $this->assertEqual($entity_duplicate->{$property}->getValue(), $entity->{$property}->getValue()); } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php index 8ee7478..a6cbbed 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php @@ -117,9 +117,9 @@ public function providerTestSet() { */ public function providerTestGetKeys() { return array( - array(array(), array('revision' => '', 'bundle' => '', 'langcode' => '')), - array(array('id' => 'id'), array('id' => 'id', 'revision' => '', 'bundle' => '', 'langcode' => '')), - array(array('bundle' => 'bundle'), array('bundle' => 'bundle', 'revision' => '', 'langcode' => '')), + array(array(), array('revision' => '', 'revision_uuid' => '', 'bundle' => '', 'langcode' => '')), + array(array('id' => 'id'), array('id' => 'id', 'revision' => '', 'revision_uuid' => '', 'bundle' => '', 'langcode' => '')), + array(array('bundle' => 'bundle'), array('bundle' => 'bundle', 'revision' => '', 'revision_uuid' => '', 'langcode' => '')), ); } diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php index 02970d7..167ca98 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php @@ -92,6 +92,13 @@ class SqlContentEntityStorageTest extends UnitTestCase { protected $connection; /** + * The UUID service. + * + * @var \Drupal\Component\Uuid\UuidInterface + */ + protected $uuidGenerator; + + /** * {@inheritdoc} */ protected function setUp() { @@ -113,6 +120,7 @@ protected function setUp() { $this->connection = $this->getMockBuilder('Drupal\Core\Database\Connection') ->disableOriginalConstructor() ->getMock(); + $this->uuidGenerator = $this->getMock('Drupal\Component\Uuid\UuidInterface'); } /** @@ -342,7 +350,7 @@ public function testOnEntityTypeCreate() { ->will($this->returnValue($schema_handler)); $storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage') - ->setConstructorArgs(array($this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager)) + ->setConstructorArgs(array($this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager, $this->uuidGenerator)) ->setMethods(array('getStorageSchema')) ->getMock(); @@ -520,6 +528,7 @@ public function testGetTableMappingRevisionable(array $entity_keys) { 'revision' => 'test_revision', 'bundle' => $entity_keys['bundle'], 'uuid' => $entity_keys['uuid'], + 'revision_uuid' => 'test_revision_uuid', ); $this->entityType->expects($this->exactly(2)) @@ -532,6 +541,7 @@ public function testGetTableMappingRevisionable(array $entity_keys) { array('uuid', $entity_keys['uuid']), array('bundle', $entity_keys['bundle']), array('revision', $entity_keys['revision']), + array('revision_uuid', $entity_keys['revision_uuid']), ))); $this->setUpEntityStorage(); @@ -543,7 +553,7 @@ public function testGetTableMappingRevisionable(array $entity_keys) { $expected = array_values(array_filter($entity_keys)); $this->assertEquals($expected, $mapping->getFieldNames('entity_test')); - $expected = array($entity_keys['id'], $entity_keys['revision']); + $expected = array($entity_keys['id'], $entity_keys['revision'], $entity_keys['revision_uuid']); $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision')); $this->assertEquals(array(), $mapping->getExtraColumns('entity_test')); @@ -568,6 +578,7 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) { 'revision' => 'test_revision', 'bundle' => $entity_keys['bundle'], 'uuid' => $entity_keys['uuid'], + 'revision_uuid' => 'test_revision_uuid', ); // PHPUnit does not allow for multiple data providers. @@ -602,6 +613,7 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) { array('uuid', $entity_keys['uuid']), array('bundle', $entity_keys['bundle']), array('revision', $entity_keys['revision']), + array('revision_uuid', $entity_keys['revision_uuid']), ))); $this->setUpEntityStorage(); @@ -613,7 +625,7 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) { $this->assertEquals($field_names, $mapping->getFieldNames('entity_test')); $expected = array_merge( - array($entity_keys['id'], $entity_keys['revision']), + array($entity_keys['id'], $entity_keys['revision'], $entity_keys['revision_uuid']), $revisionable_field_names, $revision_metadata_field_names ); @@ -756,6 +768,7 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys) $entity_keys = array( 'id' => $entity_keys['id'], 'revision' => 'test_revision', + 'revision_uuid' => 'test_revision_uuid', 'bundle' => $entity_keys['bundle'], 'uuid' => $entity_keys['uuid'], 'langcode' => 'langcode', @@ -777,6 +790,7 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys) array('uuid', $entity_keys['uuid']), array('bundle', $entity_keys['bundle']), array('revision', $entity_keys['revision']), + array('revision_uuid', $entity_keys['revision_uuid']), array('langcode', $entity_keys['langcode']), ))); @@ -798,6 +812,7 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys) $entity_keys['revision'], $entity_keys['bundle'], $entity_keys['uuid'], + $entity_keys['revision_uuid'], $entity_keys['langcode'], ))); $actual = $mapping->getFieldNames('entity_test'); @@ -807,6 +822,7 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys) $expected = array_values(array_filter(array( $entity_keys['id'], $entity_keys['revision'], + $entity_keys['revision_uuid'], $entity_keys['langcode'], ))); $actual = $mapping->getFieldNames('entity_test_revision'); @@ -856,6 +872,7 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent $entity_keys = array( 'id' => $entity_keys['id'], 'revision' => 'test_revision', + 'revision_uuid' => 'test_revision_uuid', 'bundle' => $entity_keys['bundle'], 'uuid' => $entity_keys['uuid'], 'langcode' => 'langcode', @@ -898,6 +915,7 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent array('uuid', $entity_keys['uuid']), array('bundle', $entity_keys['bundle']), array('revision', $entity_keys['revision']), + array('revision_uuid', $entity_keys['revision_uuid']), array('langcode', $entity_keys['langcode']), ))); @@ -927,6 +945,7 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent $entity_keys['revision'], $entity_keys['bundle'], $entity_keys['uuid'], + $entity_keys['revision_uuid'], $entity_keys['langcode'], ))); $actual = $mapping->getFieldNames('entity_test'); @@ -936,6 +955,7 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent $expected = array_merge(array_filter(array( $entity_keys['id'], $entity_keys['revision'], + $entity_keys['revision_uuid'], $entity_keys['langcode'], )), $revision_metadata_field_names); $actual = $mapping->getFieldNames('entity_test_revision'); @@ -1085,7 +1105,7 @@ protected function setUpEntityStorage() { ->method('getBaseFieldDefinitions') ->will($this->returnValue($this->fieldDefinitions)); - $this->entityStorage = new SqlContentEntityStorage($this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager); + $this->entityStorage = new SqlContentEntityStorage($this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager, $this->uuidGenerator); } /** @@ -1162,7 +1182,7 @@ public function testLoadMultipleNoPersistentCache() { ->method('set'); $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage') - ->setConstructorArgs(array($this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager)) + ->setConstructorArgs(array($this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager, $this->uuidGenerator)) ->setMethods(array('getFromStorage', 'invokeStorageLoadHook')) ->getMock(); $entity_storage->method('invokeStorageLoadHook') @@ -1214,7 +1234,7 @@ public function testLoadMultiplePersistentCacheMiss() { ->with($key, $entity, CacheBackendInterface::CACHE_PERMANENT, array($this->entityTypeId . '_values', 'entity_field_info')); $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage') - ->setConstructorArgs(array($this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager)) + ->setConstructorArgs(array($this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager, $this->uuidGenerator)) ->setMethods(array('getFromStorage', 'invokeStorageLoadHook')) ->getMock(); $entity_storage->method('invokeStorageLoadHook') @@ -1271,7 +1291,7 @@ public function testHasData() { ->method('getBaseFieldDefinitions') ->will($this->returnValue($this->fieldDefinitions)); - $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityManager, $this->cache, $this->languageManager); + $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityManager, $this->cache, $this->languageManager, $this->uuidGenerator); $result = $this->entityStorage->hasData();