diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php index 3cfc34b..0d17bef 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php @@ -131,7 +131,16 @@ public function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_t $entity_type->isRevisionable() != $original->isRevisionable() || $entity_type->isTranslatable() != $original->isTranslatable() || // Detect changes in key or index definitions. - $this->getEntitySchemaData($entity_type, $this->getEntitySchema($entity_type, TRUE)) != $this->loadEntitySchemaData($original); + $this->getEntitySchemaData($entity_type, $this->getEntitySchema($entity_type, TRUE)) != $this->loadEntitySchemaData($original) || + $this->hasSharedTableNameChanges($entity_type, $original); + } + + protected function hasSharedTableNameChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original) { + return + $entity_type->getBaseTable() != $original->getBaseTable() || + $entity_type->getDataTable() != $original->getDataTable() || + $entity_type->getRevisionTable() != $original->getRevisionTable() || + $entity_type->getRevisionDataTable() != $original->getRevisionDataTable(); } /** diff --git a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php index e594523..25da8f2 100644 --- a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php +++ b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php @@ -61,6 +61,10 @@ protected function updateEntityTypeToTranslatable() { $entity_type->set('translatable', TRUE); $entity_type->set('data_table', 'entity_test_update_data'); + if ($entity_type->isRevisionable()) { + $entity_type->set('revision_data_table', 'entity_test_update_revision_data'); + } + $this->state->set('entity_test_update.entity_type', $entity_type); $this->entityManager->clearCachedDefinitions(); } @@ -74,6 +78,10 @@ protected function updateEntityTypeToNotTranslatable() { $entity_type->set('translatable', FALSE); $entity_type->set('data_table', NULL); + if ($entity_type->isRevisionable()) { + $entity_type->set('revision_data_table', NULL); + } + $this->state->set('entity_test_update.entity_type', $entity_type); $this->entityManager->clearCachedDefinitions(); } @@ -199,6 +207,30 @@ protected function renameDataTable() { } /** + * Renames the revision table to 'entity_test_update_revision_new'. + */ + protected function renameRevisionBaseTable() { + $entity_type = clone $this->entityManager->getDefinition('entity_test_update'); + + $entity_type->set('revision_table', 'entity_test_update_revision_new'); + + $this->state->set('entity_test_update.entity_type', $entity_type); + $this->entityManager->clearCachedDefinitions(); + } + + /** + * Renames the revision data table to 'entity_test_update_revision_data_new'. + */ + protected function renameRevisionDataTable() { + $entity_type = clone $this->entityManager->getDefinition('entity_test_update'); + + $entity_type->set('revision_data_table', 'entity_test_update_revision_data_new'); + + $this->state->set('entity_test_update.entity_type', $entity_type); + $this->entityManager->clearCachedDefinitions(); + } + + /** * Removes the entity type. */ protected function deleteEntityType() { diff --git a/core/modules/views/src/EventSubscriber/ViewsEntitySchemaSubscriber.php b/core/modules/views/src/EventSubscriber/ViewsEntitySchemaSubscriber.php index 84dcb73..d70c288 100644 --- a/core/modules/views/src/EventSubscriber/ViewsEntitySchemaSubscriber.php +++ b/core/modules/views/src/EventSubscriber/ViewsEntitySchemaSubscriber.php @@ -115,7 +115,7 @@ public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeI } // Checks for data tables. - if ($original->getDataTable() !== NULL && $entity_type->getDataTable() != $original->getDataTable()) { + if ($entity_type->getDataTable() !== NULL && $original->getDataTable() !== NULL && $entity_type->getDataTable() != $original->getDataTable()) { $changes[] = static::DATA_TABLE_RENAME; } elseif ($original->getDataTable() === NULL && $entity_type->getDataTable()) { @@ -132,12 +132,12 @@ public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeI elseif ($original->getRevisionTable() === NULL && $entity_type->getRevisionTable()) { $changes[] = static::REVISION_TABLE_ADDITION; } - elseif ($original->getRevisionTable() && $entity_type->getRevisionTable() === NULL) { + elseif ($original->isRevisionable() && !$entity_type->isRevisionable()) { $changes[] = static::REVISION_TABLE_REMOVAL; } // Checks for revision data tables. - if ($original->getRevisionDataTable() !== NULL && $entity_type->getRevisionDataTable() != $original->getRevisionDataTable()) { + if ($entity_type->getRevisionDataTable() !== NULL && $original->getRevisionDataTable() !== NULL && $entity_type->getRevisionDataTable() != $original->getRevisionDataTable()) { $changes[] = static::REVISION_DATA_TABLE_RENAME; } elseif ($original->getRevisionDataTable() === NULL && $entity_type->getRevisionDataTable()) { @@ -155,27 +155,32 @@ public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeI $this->baseTableRename($all_views, $entity_type->id(), $original->getBaseTable(), $entity_type->getBaseTable()); break; case static::DATA_TABLE_RENAME: - $this->baseTableRename($all_views, $entity_type->id(), $original->getBaseTable(), $entity_type->getBaseTable()); $this->dataTableRename($all_views, $entity_type->id(), $original->getDataTable(), $entity_type->getDataTable()); break; case static::DATA_TABLE_ADDITION: - $this->dataTableAddition($all_views, $entity_type, $original, $original->getDataTable()); + $this->dataTableAddition($all_views, $entity_type, $entity_type->getDataTable(),$entity_type->getBaseTable()); break; case static::DATA_TABLE_REMOVAL: - $this->dataTableRemoval($all_views, $entity_type->id(), $entity_type->getDataTable(), $entity_type->getBaseTable()); + $this->dataTableRemoval($all_views, $entity_type->id(), $original->getDataTable(), $entity_type->getBaseTable()); break; - // @TODO Implement / decide about all those revision cases. case static::REVISION_TABLE_RENAME: + // @FIXME the update on revision table rename is not triggered. + $this->baseTableRename($all_views, $entity_type->id(), $original->getRevisionTable(), $entity_type->getRevisionTable()); break; case static::REVISION_TABLE_ADDITION: + // If we add revision support we don't have to do anything. break; case static::REVISION_TABLE_REMOVAL: + $this->revisionRemoval($all_views, $original); break; case static::REVISION_DATA_TABLE_RENAME: + $this->dataTableRename($all_views, $entity_type->id(), $original->getRevisionDataTable(), $entity_type->getRevisionDataTable()); break; case static::REVISION_DATA_TABLE_ADDITION: + $this->dataTableAddition($all_views, $entity_type, $entity_type->getRevisionDataTable(),$entity_type->getRevisionTable()); break; case static::REVISION_DATA_TABLE_REMOVAL: + $this->dataTableRemoval($all_views, $entity_type->id(), $original->getRevisionDataTable(), $entity_type->getRevisionTable()); break; } } @@ -297,8 +302,8 @@ protected function baseTableRename($all_views, $entity_type_id, $old_base_table, */ protected function dataTableRename($all_views, $entity_type_id, $old_data_table, $new_data_table) { foreach ($all_views as $view) { - if ($view->get('data_table') == $old_data_table) { - $view->set('data_table', $new_data_table); + if ($view->get('base_table') == $old_data_table) { + $view->set('base_table', $new_data_table); } foreach (array_keys($view->get('display')) as $display_id) { @@ -326,16 +331,16 @@ protected function dataTableRename($all_views, $entity_type_id, $old_data_table, * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type * The entity type. */ - protected function dataTableAddition($all_views, EntityTypeInterface $entity_type) { + protected function dataTableAddition($all_views, EntityTypeInterface $entity_type, $new_data_table, $base_table) { /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */ $entity_type_id = $entity_type->id(); $storage = $this->entityManager->getStorage($entity_type_id); $storage->setEntityType($entity_type); $table_mapping = $storage->getTableMapping(); - $data_table_fields = $table_mapping->getFieldNames($entity_type->getDataTable()); - $base_table_fields = $table_mapping->getFieldNames($entity_type->getBaseTable()); + $data_table_fields = $table_mapping->getFieldNames($new_data_table); + $base_table_fields = $table_mapping->getFieldNames($base_table); - $data_table = $entity_type->getDataTable(); + $data_table = $new_data_table; foreach ($all_views as $view) { foreach (array_keys($view->get('display')) as $display_id) { @@ -348,7 +353,7 @@ protected function dataTableAddition($all_views, EntityTypeInterface $entity_typ foreach ($display['display_options'][$handler_type] as $id => &$handler_config) { if (isset($handler_config['entity_type']) && isset($handler_config['entity_field']) && $handler_config['entity_type'] == $entity_type_id) { // Move all fields which just exists on the data table. - if (in_array($handler_config['entity_field'], $data_table_fields) && !in_array($handler_config['entity_field'], $base_table_fields)) { + if ($handler_config['table'] == $base_table && in_array($handler_config['entity_field'], $data_table_fields) && !in_array($handler_config['entity_field'], $base_table_fields)) { $handler_config['table'] = $data_table; } } @@ -371,6 +376,7 @@ protected function dataTableAddition($all_views, EntityTypeInterface $entity_typ * The name of the base table. */ protected function dataTableRemoval($all_views, $entity_type_id, $old_data_table, $base_table) { + // We move back the data table back to the base table. foreach ($all_views as $view) { foreach (array_keys($view->get('display')) as $display_id) { $display = &$view->getDisplay($display_id); @@ -422,4 +428,29 @@ protected function removeFieldStorage($all_views, $entity_type_id, $field_name) } } + /** + * Updates views if revision support is removed + * + * @param \Drupal\views\Entity\View[] $all_views + * All views. + * @param \Drupal\Core\Entity\EntityTypeInterface $original + * The origin entity type. + */ + protected function revisionRemoval($all_views, EntityTypeInterface $original) { + $revision_base_table = $original->getRevisionTable(); + $revision_data_table = $original->getRevisionDataTable(); + + foreach ($all_views as $view) { + if (in_array($view->get('base_table'), [$revision_base_table, $revision_data_table])) { + // Let's disable the views as we no longer support revisions. + $view->setStatus(FALSE); + // @todo Discuss whether we might even want to remove them. In general + // though we should avoid changing the system silently into a + // unrecoverable state. + } + + // For any kind of field, let's rely on the broken handler functionality. + } + } + } diff --git a/core/modules/views/src/Tests/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php b/core/modules/views/src/Tests/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php index 617fe21..b5abdc4 100644 --- a/core/modules/views/src/Tests/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php +++ b/core/modules/views/src/Tests/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php @@ -39,7 +39,7 @@ class ViewsEntitySchemaSubscriberIntegrationTest extends ViewUnitTestBase { * * @var array */ - public static $testViews = ['test_view_entity_test', 'test_view_entity_test_data', 'test_view_entity_test_additional_base_field']; + public static $testViews = ['test_view_entity_test', 'test_view_entity_test_revision', 'test_view_entity_test_data', 'test_view_entity_test_additional_base_field']; /** * The event dispatcher. @@ -100,7 +100,7 @@ public function testDeleteEntityType() { $views = $entity_storage->loadMultiple(); // User module provides 3 views for themselves. - $this->assertEqual(6, count($views)); + $this->assertEqual(7, count($views)); $event = new EntityTypeEvent($this->entityManager->getDefinition('entity_test_update')); $this->eventDispatcher->dispatch(EntityTypeEvents::DELETE, $event); @@ -108,7 +108,7 @@ public function testDeleteEntityType() { // We expect that views which use 'entity_test_update' as base tables are // disabled. $views = $entity_storage->loadMultiple(); - $this->assertEqual(6, count($views)); + $this->assertEqual(7, count($views)); $this->assertFalse($views['test_view_entity_test']->status()); $this->assertFalse($views['test_view_entity_test_data']->status()); @@ -155,6 +155,49 @@ public function testDataTableRename() { } /** + * Tests that renaming revision tables adapts the views. + */ + public function testRevisionBaseTableRename() { + $this->updateEntityTypeToRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + $this->renameRevisionBaseTable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_revision'); + + // Ensure the base table got renamed, so also the views fields. + $this->assertEqual('entity_test_update_revision_new', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update_revision_new', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision_new', $display['display_options']['fields']['name']['table']); + } + + /** + * Tests that renaming revision tables adapts the views. + */ + public function testRevisionDataTableRename() { + $this->updateEntityTypeToRevisionable(); + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + $this->renameRevisionDataTable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_revision'); + + // Ensure the base table got renamed, so also the views fields. + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision_data_new', $display['display_options']['fields']['name']['table']); + } + + /** * Tests that adding data tables adapts the views. */ public function testDataTableAddition() { @@ -191,6 +234,23 @@ public function testRevisionEnabling() { } /** + * Tests that removing revision support disables the view. + */ + public function testRevisionDisabling() { + $this->updateEntityTypeToRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + $this->updateEntityTypeToNotRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_revision'); + + $this->assertFalse($view->status()); + } + + /** * Tests that removing fields adapts the existing views. */ public function testRemoveFields() { @@ -334,13 +394,41 @@ public function testVariousTableUpdates() { $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); } + public function testVariousTableUpdatesForRevisionView() { + // base + revision <-> base + translation + revision + $this->updateEntityTypeToRevisionable(); + list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE); + + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE); + + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision_data', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToNotTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE); + + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']); + + $this->resetEntityType(); + } + /** * Gets a view and its display. */ - protected function getUpdatedViewAndDisplay() { + protected function getUpdatedViewAndDisplay($revision = FALSE) { $entity_storage = $this->entityManager->getStorage('view'); /** @var \Drupal\views\Entity\View $view */ - $view = $entity_storage->load('test_view_entity_test'); + $view = $entity_storage->load($revision ? 'test_view_entity_test_revision' : 'test_view_entity_test'); $display = $view->getDisplay('default'); return [$view, $display]; diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_entity_test_revision.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_entity_test_revision.yml new file mode 100644 index 0000000..e80028b --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_entity_test_revision.yml @@ -0,0 +1,67 @@ +langcode: und +status: true +dependencies: { } +id: test_view_entity_test_revision +label: '' +module: views +description: '' +tag: '' +base_table: entity_test_update_revision +base_field: nid +core: '8' +display: + default: + display_options: + access: + type: none + cache: + type: none + exposed_form: + type: basic + fields: + id: + alter: + alter_text: false + ellipsis: true + html: false + make_link: false + strip_tags: false + trim: false + word_boundary: true + empty_zero: false + field: id + hide_empty: false + id: id + table: entity_test_update_revision + entity_type: entity_test_update + entity_field: id + plugin_id: numeric + name: + alter: + alter_text: false + ellipsis: true + html: false + make_link: false + strip_tags: false + trim: false + word_boundary: true + empty_zero: false + field: name + hide_empty: false + id: name + table: entity_test_update_revision + plugin_id: standard + entity_type: entity_test_update + entity_field: name + pager: + type: some + style: + type: default + row: + type: fields + field_langcode: '***LANGUAGE_language_content***' + field_langcode_add_to_query: null + display_plugin: default + display_title: Master + id: default + position: 0