Commit 007eaf35 authored by catch's avatar catch

Issue #2392351 by alexpott, swentel: When an entity bundle config gets...

Issue #2392351 by alexpott, swentel: When an entity bundle config gets deleted, entities of that bundle break
parent 3359738e
......@@ -322,6 +322,11 @@ services:
entity.form_builder:
class: Drupal\Core\Entity\EntityFormBuilder
arguments: ['@entity.manager', '@form_builder']
entity.bundle_config_import_validator:
class: Drupal\Core\Entity\Event\BundleConfigImportValidate
arguments: ['@config.manager', '@entity.manager']
tags:
- { name: event_subscriber }
plugin.manager.block:
class: Drupal\Core\Block\BlockManager
parent: default_plugin_manager
......
......@@ -37,4 +37,23 @@ public function getConfigImporter() {
return $this->configImporter;
}
/**
* Gets the list of changes that will be imported.
*
* @param string $op
* (optional) A change operation. Either delete, create or update. If
* supplied the returned list will be limited to this operation.
* @param string $collection
* (optional) The collection to get the changelist for. Defaults to the
* default collection.
*
* @return array
* An array of config changes that are yet to be imported.
*
* @see \Drupal\Core\Config\StorageComparerInterface::getChangelist()
*/
public function getChangelist($op = NULL, $collection = StorageInterface::DEFAULT_COLLECTION) {
return $this->configImporter->getStorageComparer()->getChangelist($op, $collection);
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Entity\Event\BundleConfigImportValidate
*/
namespace Drupal\Core\Entity\Event;
use Drupal\Core\Config\ConfigImporterEvent;
use Drupal\Core\Config\ConfigImportValidateEventSubscriberBase;
use Drupal\Core\Config\ConfigManagerInterface;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\Core\Entity\EntityManagerInterface;
/**
* Entity config importer validation event subscriber.
*/
class BundleConfigImportValidate extends ConfigImportValidateEventSubscriberBase {
/**
* The config manager.
*
* @var \Drupal\Core\Config\ConfigManagerInterface
*/
protected $configManager;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Constructs the event subscriber.
*
* @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
* The config manager
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
*/
public function __construct(ConfigManagerInterface $config_manager, EntityManagerInterface $entity_manager) {
$this->configManager = $config_manager;
$this->entityManager = $entity_manager;
}
/**
* Ensures bundles that will be deleted are not in use.
*
* @param \Drupal\Core\Config\ConfigImporterEvent $event
* The config import event.
*/
public function onConfigImporterValidate(ConfigImporterEvent $event) {
foreach ($event->getChangelist('delete') as $config_name) {
// Get the config entity type ID. This also ensure we are dealing with a
// configuration entity.
if ($entity_type_id = $this->configManager->getEntityTypeIdByName($config_name)) {
$entity_type = $this->entityManager->getDefinition($entity_type_id);
// Does this entity type define a bundle of another entity type.
if ($bundle_of = $entity_type->getBundleOf()) {
// Work out if there are entities with this bundle.
$bundle_of_entity_type = $this->entityManager->getDefinition($bundle_of);
$bundle_id = ConfigEntityStorage::getIDFromConfigName($config_name, $entity_type->getConfigPrefix());
$entity_query = $this->entityManager->getStorage($bundle_of)->getQuery();
$entity_ids = $entity_query->condition($bundle_of_entity_type->getKey('bundle'), $bundle_id)
->accessCheck(FALSE)
->range(0, 1)
->execute();
if (!empty($entity_ids)) {
$entity = $this->entityManager->getStorage($entity_type_id)->load($bundle_id);
$event->getConfigImporter()->logError($this->t('Entities exist of type %entity_type and %bundle_label %bundle. These entities need to be deleted before importing.', array('%entity_type' => $bundle_of_entity_type->getLabel(), '%bundle_label' => $bundle_of_entity_type->getBundleLabel(), '%bundle' => $entity->label())));
}
}
}
}
}
}
......@@ -393,4 +393,48 @@ function testImportErrorLog() {
$this->assertText(t('There are no configuration changes to import.'));
}
/**
* Tests the config importer cannot delete bundles with existing entities.
*
* @see \Drupal\Core\Entity\Event\BundleConfigImportValidate
*/
public function testEntityBundleDelete() {
\Drupal::service('module_installer')->install(array('node'));
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
$node_type = $this->drupalCreateContentType();
$node = $this->drupalCreateNode(array('type' => $node_type->id()));
$this->drupalGet('admin/config/development/configuration');
// The node type, body field and entity displays will be scheduled for
// removal.
$this->assertText(format_string('node.type.@type', array('@type' => $node_type->id())));
$this->assertText(format_string('field.field.node.@type.body', array('@type' => $node_type->id())));
$this->assertText(format_string('core.entity_view_display.node.@type.teaser', array('@type' => $node_type->id())));
$this->assertText(format_string('core.entity_view_display.node.@type.default', array('@type' => $node_type->id())));
$this->assertText(format_string('core.entity_form_display.node.@type.default', array('@type' => $node_type->id())));
// Attempt to import configuration and verify that an error message appears
// and the node type, body field and entity displays are still scheduled for
// removal.
$this->drupalPostForm(NULL, array(), t('Import all'));
$validation_message = t('Entities exist of type %entity_type and %bundle_label %bundle. These entities need to be deleted before importing.', array('%entity_type' => $node->getEntityType()->getLabel(), '%bundle_label' => $node->getEntityType()->getBundleLabel(), '%bundle' => $node_type->label()));
$this->assertRaw($validation_message);
$this->assertText(format_string('node.type.@type', array('@type' => $node_type->id())));
$this->assertText(format_string('field.field.node.@type.body', array('@type' => $node_type->id())));
$this->assertText(format_string('core.entity_view_display.node.@type.teaser', array('@type' => $node_type->id())));
$this->assertText(format_string('core.entity_view_display.node.@type.default', array('@type' => $node_type->id())));
$this->assertText(format_string('core.entity_form_display.node.@type.default', array('@type' => $node_type->id())));
// Delete the node and try to import again.
$node->delete();
$this->drupalPostForm(NULL, array(), t('Import all'));
$this->assertNoRaw($validation_message);
$this->assertText(t('There are no configuration changes to import.'));
$this->assertNoText(format_string('node.type.@type', array('@type' => $node_type->id())));
$this->assertNoText(format_string('field.field.node.@type.body', array('@type' => $node_type->id())));
$this->assertNoText(format_string('core.entity_view_display.node.@type.teaser', array('@type' => $node_type->id())));
$this->assertNoText(format_string('core.entity_view_display.node.@type.default', array('@type' => $node_type->id())));
$this->assertNoText(format_string('core.entity_form_display.node.@type.default', array('@type' => $node_type->id())));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment