diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php index ec3ebaf..a12f773 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php @@ -1138,6 +1138,13 @@ protected function createSharedTableSchema(FieldStorageDefinitionInterface $stor $schema[$table_name] = $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names); if (!$only_save) { foreach ($schema[$table_name]['fields'] as $name => $specifier) { + // Use the value of the entity ID as the initial value for the + // revision ID field. + if ($created_field_name == $this->entityType->getKey('revision')) { + $id_column_name = $table_mapping->getColumnNames($this->entityType->getKey('id')); + $specifier['initial_from_field'] = reset($id_column_name); + } + // Check if the field exists because it might already have been // created as part of the earlier entity type update event. if (!$schema_handler->fieldExists($table_name, $name)) { diff --git a/core/modules/shortcut/shortcut.install b/core/modules/shortcut/shortcut.install index 8df408f..68d834c 100644 --- a/core/modules/shortcut/shortcut.install +++ b/core/modules/shortcut/shortcut.install @@ -67,3 +67,14 @@ function shortcut_uninstall() { \Drupal::configFactory()->getEditable('seven.settings')->clear('third_party_settings.shortcut')->save(TRUE); } } + +/** + * Update schema. + */ +function shortcut_update_8001() { + $entity_type = \Drupal::entityTypeManager()->getStorage('shortcut')->getEntityType(); + \Drupal::service('system.entity_schema_updater')->updateEntityType($entity_type); + \Drupal::service('system.entity_schema_updater')->createTables($entity_type); + \Drupal::service('system.entity_schema_updater')->installFields($entity_type); + \Drupal::service('system.entity_schema_updater')->copyData($entity_type); +} diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php index 6ddaf05..9f60fe0 100644 --- a/core/modules/shortcut/src/Entity/Shortcut.php +++ b/core/modules/shortcut/src/Entity/Shortcut.php @@ -3,9 +3,9 @@ namespace Drupal\shortcut\Entity; use Drupal\Core\Cache\Cache; -use Drupal\Core\Entity\ContentEntityBase; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Entity\RevisionableContentEntityBase; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\link\LinkItemInterface; use Drupal\shortcut\ShortcutInterface; @@ -30,9 +30,12 @@ * }, * base_table = "shortcut", * data_table = "shortcut_field_data", + * revision_table = "shortcut_revision", + * revision_data_table = "shortcut_field_revision", * translatable = TRUE, * entity_keys = { * "id" = "id", + * "revision" = "revision_id", * "uuid" = "uuid", * "bundle" = "shortcut_set", * "label" = "title", @@ -47,7 +50,7 @@ * bundle_entity_type = "shortcut_set" * ) */ -class Shortcut extends ContentEntityBase implements ShortcutInterface { +class Shortcut extends RevisionableContentEntityBase implements ShortcutInterface { /** * {@inheritdoc} @@ -122,6 +125,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDescription(t('The name of the shortcut.')) ->setRequired(TRUE) ->setTranslatable(TRUE) + ->setRevisionable(TRUE) ->setSetting('max_length', 255) ->setDisplayOptions('form', array( 'type' => 'string_textfield', @@ -139,6 +143,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setLabel(t('Path')) ->setDescription(t('The location this shortcut points to.')) ->setRequired(TRUE) + ->setRevisionable(TRUE) ->setSettings(array( 'link_type' => LinkItemInterface::LINK_INTERNAL, 'title' => DRUPAL_DISABLED, diff --git a/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php b/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php index 0d58c31..c8517ce 100644 --- a/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php +++ b/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php @@ -21,6 +21,7 @@ public function testInstallUninstall() { \Drupal::service('theme_installer')->install(['seven']); $this->assertNull($this->config('seven.settings')->get('third_party_settings.shortcut'), 'There are no shortcut settings in seven.settings.'); + \Drupal::service('module_installer')->install(['user']); \Drupal::service('module_installer')->install(['shortcut']); $this->assertTrue($this->config('seven.settings')->get('third_party_settings.shortcut.module_link'), 'The shortcut module_link setting is in seven.settings.'); diff --git a/core/modules/system/src/EntitySchemaUpdater.php b/core/modules/system/src/EntitySchemaUpdater.php new file mode 100644 index 0000000..1fc6ba6 --- /dev/null +++ b/core/modules/system/src/EntitySchemaUpdater.php @@ -0,0 +1,186 @@ +lastInstalledSchemaRepository = $last_installed_schema_repository; + $this->entityTypeManager = $entity_type_manager; + $this->entityDefinitionUpdateManager = $entity_definition_update_manager; + $this->entityFieldManager = $entity_field_manager; + $this->database = $database; + } + + /** + * {@inheritdoc} + */ + public function updateEntityType(EntityTypeInterface $entity_type) { + $last_entity_type = $this->lastInstalledSchemaRepository->getLastInstalledDefinition($entity_type->id()); + $keys = $last_entity_type->getKeys(); + $keys['revision'] = 'revision_id'; + $last_entity_type->set('entity_keys', $keys); + $last_entity_type->set('revision_table', $entity_type->getRevisionTable()); + $last_entity_type->set('revision_data_table', $entity_type->getRevisionDataTable()); + $this->lastInstalledSchemaRepository->setLastInstalledDefinition($last_entity_type); + } + + /** + * {@inheritdoc} + */ + public function createTables(EntityTypeInterface $entity_type) { + /** @var \Drupal\Core\Entity\EntityTypeListenerInterface $storage */ + $storage = $this->entityTypeManager->getStorage($entity_type->id()); + if ($storage instanceof EntityTypeListenerInterface) { + $storage->onEntityTypeCreate($entity_type); + } + } + + /** + * {@inheritdoc} + */ + public function installFields(EntityTypeInterface $entity_type) { + if ($entity_type->hasKey('revision')) { + $revision_id = BaseFieldDefinition::create('integer') + ->setLabel(new TranslatableMarkup('Revision ID')) + ->setReadOnly(TRUE) + ->setSetting('unsigned', TRUE); + $this->entityDefinitionUpdateManager + ->installFieldStorageDefinition($entity_type->getKey('revision'), $entity_type->id(), $entity_type->id(), $revision_id); + + } + + $revision_created = BaseFieldDefinition::create('created') + ->setLabel(t('Revision create time')) + ->setDescription(t('The time that the current revision was created.')) + ->setRevisionable(TRUE); + $this->entityDefinitionUpdateManager + ->installFieldStorageDefinition("revision_created", $entity_type->id(), $entity_type->id(), $revision_created); + + $revision_user = BaseFieldDefinition::create('entity_reference') + ->setLabel(t('Revision user')) + ->setDescription(t('The user ID of the author of the current revision.')) + ->setSetting('target_type', 'user') + ->setRevisionable(TRUE); + $this->entityDefinitionUpdateManager + ->installFieldStorageDefinition("revision_user", $entity_type->id(), $entity_type->id(), $revision_user); + + $revision_log_message = BaseFieldDefinition::create('string_long') + ->setLabel(t('Revision log message')) + ->setDescription(t('Briefly describe the changes you have made.')) + ->setRevisionable(TRUE) + ->setDefaultValue('') + ->setDisplayOptions('form', [ + 'type' => 'string_textarea', + 'weight' => 25, + 'settings' => [ + 'rows' => 4, + ], + ]); + $this->entityDefinitionUpdateManager + ->installFieldStorageDefinition("revision_log_message", $entity_type->id(), $entity_type->id(), $revision_log_message); + + /** @var \Drupal\Core\Field\FieldDefinitionInterface[] $field_definitions */ + $field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type->id()); + foreach ($field_definitions as $field_definition) { + $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($field_definition); + } + + $revision_data_table = $entity_type->getRevisionDataTable(); + $schema = $this->database->schema(); + $schema->dropPrimaryKey($revision_data_table); + $schema->addPrimaryKey($revision_data_table, [$entity_type->getKey('revision'), $entity_type->getKey('langcode')]); + } + + /** + * {@inheritdoc} + */ + public function copyData(EntityTypeInterface $entity_type) { + /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $table_mapping */ + $table_mapping = $this->entityTypeManager->getStorage($entity_type->id())->getTableMapping(); + $table_names = $table_mapping->getTableNames(); + $data = []; + foreach ($table_names as $table_name) { + $column_names = $table_mapping->getAllColumns($table_name); + $results = $this->database->select($table_name, 't')->fields('t')->execute()->fetchAll(); + foreach ($results as $key => $result) { + foreach ($column_names as $column_name) { + if (!empty($result->{$column_name})) { + $data[$key][$column_name] = $result->{$column_name}; + } + } + } + } + + $id = $entity_type->getKey('id'); + $revision_id = $entity_type->getKey('revision'); + foreach ($data as $record) { + if (!empty($record[$id])) { + $record[$revision_id] = $record[$id]; + } + foreach ($table_names as $table_name) { + $values = []; + $column_names = $table_mapping->getAllColumns($table_name); + foreach ($column_names as $column_name) { + if (!empty($record[$column_name])) { + $values[$column_name] = $record[$column_name]; + } + } + $query = $this->database->select($table_name)->fields($table_name, [$id])->condition($id, $record[$id]); + $exists = $query->execute()->fetchAll(); + if (isset($exists) && isset($exists[0]) && ($exists[0]->{$id} == $record[$id])) { + $this->database + ->update($table_name) + ->fields($values) + ->condition($id, $record[$id]) + ->execute(); + } + else { + $this->database + ->insert($table_name) + ->fields($values) + ->execute(); + } + } + } + } +} diff --git a/core/modules/system/src/EntitySchemaUpdaterInterface.php b/core/modules/system/src/EntitySchemaUpdaterInterface.php new file mode 100644 index 0000000..0175528 --- /dev/null +++ b/core/modules/system/src/EntitySchemaUpdaterInterface.php @@ -0,0 +1,37 @@ +databaseDumpFiles = [__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz']; + } + + public function testMakeRevisionable() { + $entity_type = \Drupal::entityTypeManager()->getStorage('shortcut')->getEntityType(); + + /** @var ContentEntityInterface $last_installed */ + $pre_last_installed = \Drupal::service('entity.last_installed_schema.repository')->getLastInstalledDefinition($entity_type->id()); + $this->assertFalse($pre_last_installed->isRevisionable()); + + $this->runUpdates(); + + /** @var ContentEntityInterface $last_installed */ + $post_last_installed = \Drupal::service('entity.last_installed_schema.repository')->getLastInstalledDefinition($entity_type->id()); + $this->assertTrue($post_last_installed->isRevisionable()); + + $storage = \Drupal::entityTypeManager()->getStorage($entity_type->id()); + $entities = $storage->loadMultiple(); + $this->assertEqual(count($entities), 2, "Two Shortcut entities found"); + + } + +} diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index c70889d..93ac9a8 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -43,3 +43,6 @@ services: arguments: ['@theme_handler', '@cache_tags.invalidator'] tags: - { name: event_subscriber } + system.entity_schema_updater: + class: Drupal\system\EntitySchemaUpdater + arguments: ['@entity.last_installed_schema.repository', '@entity_type.manager', '@entity.definition_update_manager', '@entity_field.manager', '@database']