entityManager = $entity_manager; } /** * {@inheritdoc} */ public static function getSubscribedEvents() { return static::getEntityTypeEvents(); } /** * {@inheritdoc} */ public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original) { $changes = []; // We implement a specific logic for table updates, which is bound to the // default sql content entity storage. if (!$this->entityManager->getStorage($entity_type->id()) instanceof SqlContentEntityStorage) { return; } if ($entity_type->getBaseTable() != $original->getBaseTable()) { $changes[] = static::BASE_TABLE_RENAME; } $revision_add = $entity_type->isRevisionable() && !$original->isRevisionable(); $revision_remove = !$entity_type->isRevisionable() && $original->isRevisionable(); $translation_add = $entity_type->isTranslatable() && !$original->isTranslatable(); $translation_remove = !$entity_type->isTranslatable() && $original->isTranslatable(); if ($revision_add) { $changes[] = static::REVISION_TABLE_ADDITION; } elseif ($revision_remove) { $changes[] = static::REVISION_TABLE_REMOVAL; } elseif ($entity_type->isRevisionable() && $entity_type->getRevisionTable() != $original->getRevisionTable()) { $changes[] = static::REVISION_TABLE_RENAME; } if ($translation_add) { $changes[] = static::DATA_TABLE_ADDITION; } elseif ($translation_remove) { $changes[] = static::DATA_TABLE_REMOVAL; } elseif ($entity_type->isTranslatable() && $entity_type->getDataTable() != $original->getDataTable()) { $changes[] = static::DATA_TABLE_RENAME; } if ($entity_type->isRevisionable() && $entity_type->isTranslatable()) { if ($revision_add || $translation_add) { $changes[] = static::REVISION_DATA_TABLE_ADDITION; } elseif ($entity_type->getRevisionDataTable() != $original->getRevisionDataTable()) { $changes[] = static::REVISION_DATA_TABLE_RENAME; } } elseif ($original->isRevisionable() && $original->isTranslatable() && ($revision_remove || $translation_remove)) { $changes[] = static::REVISION_DATA_TABLE_REMOVAL; } // Stop here if no changes are needed. if (empty($changes)) { return; } /** @var \Drupal\views\Entity\View[] $all_views */ $all_views = $this->entityManager->getStorage('view')->loadMultiple(NULL); foreach ($changes as $change) { switch ($change) { case static::BASE_TABLE_RENAME: $this->baseTableRename($all_views, $entity_type->id(), $original->getBaseTable(), $entity_type->getBaseTable()); break; case static::DATA_TABLE_RENAME: $this->dataTableRename($all_views, $entity_type->id(), $original->getDataTable(), $entity_type->getDataTable()); break; case static::DATA_TABLE_ADDITION: $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(), $original->getDataTable(), $entity_type->getBaseTable()); break; case static::REVISION_TABLE_RENAME: $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; } } foreach ($all_views as $view) { // All changes done to the views here can be trusted and this might be // called during updates, when it is not safe to rely on configuration // containing valid schema. Trust the data and disable schema validation // and casting. $view->trustData()->save(); } } /** * {@inheritdoc} */ public function onEntityTypeDelete(EntityTypeInterface $entity_type) { $tables = [ $entity_type->getBaseTable(), $entity_type->getDataTable(), $entity_type->getRevisionTable(), $entity_type->getRevisionDataTable(), ]; $all_views = $this->entityManager->getStorage('view')->loadMultiple(NULL); /** @var \Drupal\views\Entity\View $view */ foreach ($all_views as $id => $view) { // First check just the base table. if (in_array($view->get('base_table'), $tables)) { $view->disable(); $view->save(); } } } /** * Applies a callable onto all handlers of all passed in views. * * @param \Drupal\views\Entity\View[] $all_views * All views entities. * @param callable $process * A callable which retrieves a handler config array. */ protected function processHandlers(array $all_views, callable $process) { foreach ($all_views as $view) { foreach (array_keys($view->get('display')) as $display_id) { $display = &$view->getDisplay($display_id); foreach (Views::getHandlerTypes() as $handler_type) { $handler_type = $handler_type['plural']; if (!isset($display['display_options'][$handler_type])) { continue; } foreach ($display['display_options'][$handler_type] as $id => &$handler_config) { $process($handler_config); if ($handler_config === NULL) { unset($display['display_options'][$handler_type][$id]); } } } } } } /** * Updates views if a base table is renamed. * * @param \Drupal\views\Entity\View[] $all_views * All views. * @param string $entity_type_id * The entity type ID. * @param string $old_base_table * The old base table name. * @param string $new_base_table * The new base table name. */ protected function baseTableRename($all_views, $entity_type_id, $old_base_table, $new_base_table) { foreach ($all_views as $view) { if ($view->get('base_table') == $old_base_table) { $view->set('base_table', $new_base_table); } } $this->processHandlers($all_views, function (array &$handler_config) use ($entity_type_id, $old_base_table, $new_base_table) { if (isset($handler_config['entity_type']) && $handler_config['entity_type'] == $entity_type_id && $handler_config['table'] == $old_base_table) { $handler_config['table'] = $new_base_table; } }); } /** * Updates views if a data table is renamed. * * @param \Drupal\views\Entity\View[] $all_views * All views. * @param string $entity_type_id * The entity type ID. * @param string $old_data_table * The old data table name. * @param string $new_data_table * The new data table name. */ protected function dataTableRename($all_views, $entity_type_id, $old_data_table, $new_data_table) { foreach ($all_views as $view) { if ($view->get('base_table') == $old_data_table) { $view->set('base_table', $new_data_table); } } $this->processHandlers($all_views, function (array &$handler_config) use ($entity_type_id, $old_data_table, $new_data_table) { if (isset($handler_config['entity_type']) && $handler_config['entity_type'] == $entity_type_id && $handler_config['table'] == $old_data_table) { $handler_config['table'] = $new_data_table; } }); } /** * Updates views if a data table is added. * * @param \Drupal\views\Entity\View[] $all_views * All views. * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type * The entity type. * @param string $new_data_table * The new data table. * @param string $base_table * The base table. */ 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($new_data_table); $base_table_fields = $table_mapping->getFieldNames($base_table); $data_table = $new_data_table; $this->processHandlers($all_views, function (array &$handler_config) use ($entity_type_id, $base_table, $data_table, $base_table_fields, $data_table_fields) { 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 ($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; } } }); } /** * Updates views if a data table is removed. * * @param \Drupal\views\Entity\View[] $all_views * All views. * @param string $entity_type_id * The entity type ID. * @param string $old_data_table * The name of the previous existing data table. * @param string $base_table * 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. $this->processHandlers($all_views, function (array &$handler_config) use ($entity_type_id, $old_data_table, $base_table) { if (isset($handler_config['entity_type']) && $handler_config['entity_type'] == $entity_type_id) { if ($handler_config['table'] == $old_data_table) { $handler_config['table'] = $base_table; } } }); } /** * 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); } // For any kind of field, let's rely on the broken handler functionality. } } }