Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 163 |
ViewsEntitySchemaSubscriber | |
0.00% |
0 / 1 |
|
9.09% |
1 / 11 |
6162 | |
0.00% |
0 / 163 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
getSubscribedEvents | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
onEntityTypeUpdate | |
0.00% |
0 / 1 |
1332 | |
0.00% |
0 / 78 |
|||
onEntityTypeDelete | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 14 |
|||
processHandlers | |
0.00% |
0 / 1 |
56 | |
0.00% |
0 / 18 |
|||
baseTableRename | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 10 |
|||
anonymous function | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 6 |
|||
dataTableRename | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 10 |
|||
dataTableAddition | |
0.00% |
0 / 1 |
56 | |
0.00% |
0 / 14 |
|||
dataTableRemoval | |
100.00% |
1 / 1 |
4 | |
100.00% |
0 / 0 |
|||
revisionRemoval | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 8 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\views\EventSubscriber\ViewsEntitySchemaSubscriber. | |
*/ | |
namespace Drupal\views\EventSubscriber; | |
use Drupal\Core\Entity\EntityManagerInterface; | |
use Drupal\Core\Entity\EntityTypeEventSubscriberTrait; | |
use Drupal\Core\Entity\EntityTypeInterface; | |
use Drupal\Core\Entity\EntityTypeListenerInterface; | |
use Drupal\Core\Entity\Sql\SqlContentEntityStorage; | |
use Drupal\views\Views; | |
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
/** | |
* Reacts to changes on entity types to update all views entities. | |
*/ | |
class ViewsEntitySchemaSubscriber implements EntityTypeListenerInterface, EventSubscriberInterface { | |
use EntityTypeEventSubscriberTrait; | |
/** | |
* Indicates that a base table got renamed. | |
*/ | |
const BASE_TABLE_RENAME = 0; | |
/** | |
* Indicates that a data table got renamed. | |
*/ | |
const DATA_TABLE_RENAME = 1; | |
/** | |
* Indicates that a data table got added. | |
*/ | |
const DATA_TABLE_ADDITION = 2; | |
/** | |
* Indicates that a data table got removed. | |
*/ | |
const DATA_TABLE_REMOVAL = 3; | |
/** | |
* Indicates that a revision table got renamed. | |
*/ | |
const REVISION_TABLE_RENAME = 4; | |
/** | |
* Indicates that a revision table got added. | |
*/ | |
const REVISION_TABLE_ADDITION = 5; | |
/** | |
* Indicates that a revision table got removed. | |
*/ | |
const REVISION_TABLE_REMOVAL = 6; | |
/** | |
* Indicates that a revision data table got renamed. | |
*/ | |
const REVISION_DATA_TABLE_RENAME = 7; | |
/** | |
* Indicates that a revision data table got added. | |
*/ | |
const REVISION_DATA_TABLE_ADDITION = 8; | |
/** | |
* Indicates that a revision data table got removed. | |
*/ | |
const REVISION_DATA_TABLE_REMOVAL = 9; | |
/** | |
* The entity manager. | |
* | |
* @var \Drupal\Core\Entity\EntityManagerInterface | |
*/ | |
protected $entityManager; | |
/** | |
* Constructs a ViewsEntitySchemaSubscriber. | |
* | |
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | |
* The entity manager. | |
*/ | |
public function __construct(EntityManagerInterface $entity_manager) { | |
$this->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; | |
} | |
/** @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. | |
} | |
} | |
} |