diff -u b/core/lib/Drupal/Core/Config/Entity/ConfigDependencyManager.php b/core/lib/Drupal/Core/Config/Entity/ConfigDependencyManager.php --- b/core/lib/Drupal/Core/Config/Entity/ConfigDependencyManager.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigDependencyManager.php @@ -138,7 +138,8 @@ * Creates a graph of config entity dependencies. * * @param array $entities_to_check - * The entities to supply dependencies for. + * The configuration entity full configuration names to determine the + * dependencies for. * * @return \Drupal\Core\Config\Entity\ConfigEntityDependency[] * A graph of config entity dependency objects that are dependent on the @@ -192,7 +193,7 @@ * Configuration data keyed by configuration object name. Typically the * output of \Drupal\Core\Config\StorageInterface::loadMultiple(). * - * @return self + * @return $this */ public function setData(array $data) { array_walk($data, function (&$config, $name) { diff -u b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php --- b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -272,8 +272,12 @@ throw new ConfigDuplicateUUIDException(format_string('Attempt to save a configuration entity %id with UUID %uuid when this entity already exists with UUID %original_uuid', array('%id' => $this->id(), '%uuid' => $this->uuid(), '%original_uuid' => $original->uuid()))); } } - // Ensure the correct dependencies are present. - $this->calculateDependencies(); + if (!$this->isSyncing()) { + // Ensure the correct dependencies are present. If the configuration is + // being written during a configuration synchronisation then there is no + // need to recalculate the dependencies. + $this->calculateDependencies(); + } } /** @@ -334,9 +338,10 @@ */ protected function addDependency($type, $name) { // A config entity is always dependent on its provider. There is no need to - // explicitly declare the dependency. + // explicitly declare the dependency. An explicit dependency on Core, which + // provides some plugins, is also not needed. // @see \Drupal\Core\Config\Entity\ConfigEntityDependency::hasDependency() - if ($type == 'module' && $this->getEntityType()->getProvider() == $name) { + if ($type == 'module' && ($name == $this->getEntityType()->getProvider() || $name == 'Core')) { return $this; } if (empty($this->dependencies[$type])) { diff -u b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php --- b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php @@ -92,8 +92,19 @@ public function isSyncing(); /** - * Returns whether the configuration entity is being deleted by the uninstall - * process. + * Returns whether this entity is being changed during the uninstall process. + * + * If you are writing code that responds to a change in this entity (insert, + * update, delete, presave, etc.), and your code would result in a + * configuration change (whether related to this configuration entity, another + * configuration entity, or non-entity configuration) or your code would + * result in a change to this entity itself, you need to check and see if this + * entity change is part of an uninstall process, and skip executing your code + * if that is the case. + * + * For example, \Drupal\language\Entity\Language::preDelete() prevents the API + * from deleting the default language. However during an uninstall of the + * language module it is expected that the default language should be deleted. * * @return bool */ diff -u b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupInterface.php b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupInterface.php --- b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupInterface.php +++ b/core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupInterface.php @@ -51,7 +51,7 @@ /** * Gets the array of breakpoints for the breakpoint group. * - * @return \Drupal\breakpoint\Entity\Breakpoint[] + * @return \Drupal\breakpoint\Entity\BreakpointInterface[] * The array of breakpoints for the breakpoint group. */ public function getBreakpoints(); @@ -62,7 +62,7 @@ * @param string $id * The breakpoint ID to get. * - * @return \Drupal\breakpoint\Entity\Breakpoint|boolean + * @return \Drupal\breakpoint\Entity\BreakpointInterface|boolean * The breakpoint or FALSE if not in the Breakpoint group. */ public function getBreakpointById($id); diff -u b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointGroupConfigEntityUnitTest.php b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointGroupConfigEntityUnitTest.php --- b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointGroupConfigEntityUnitTest.php +++ b/core/modules/breakpoint/tests/Drupal/breakpoint/Tests/BreakpointGroupConfigEntityUnitTest.php @@ -113,8 +113,7 @@ 'sourceType' => Breakpoint::SOURCE_TYPE_MODULE, ) ); - $breakpoint = $this->getMockBuilder('\Drupal\breakpoint\Entity\Breakpoint') - ->disableOriginalConstructor()->getMock(); + $breakpoint = $this->getMock('\Drupal\breakpoint\BreakpointInterface'); $breakpoint->expects($this->once()) ->method('getConfigDependencyName') ->will($this->returnValue('breakpoint.breakpoint.test')); diff -u b/core/modules/comment/lib/Drupal/comment/Tests/Views/WizardTest.php b/core/modules/comment/lib/Drupal/comment/Tests/Views/WizardTest.php --- b/core/modules/comment/lib/Drupal/comment/Tests/Views/WizardTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/Views/WizardTest.php @@ -33,17 +33,20 @@ ); } + /** + * {@inheritdoc} + */ public function setUp() { parent::setUp(); $this->drupalCreateContentType(array('type' => 'page', 'name' => t('Basic page'))); + // Add comment field to page node type. + $this->container->get('comment.manager')->addDefaultField('node', 'page'); } /** * Tests adding a view of comments. */ public function testCommentWizard() { - // Add comment field to page node type. - $this->container->get('comment.manager')->addDefaultField('node', 'page'); $view = array(); $view['label'] = $this->randomName(16); $view['id'] = strtolower($this->randomName(16)); diff -u b/core/modules/config/lib/Drupal/config/Tests/ConfigDependencyTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigDependencyTest.php --- b/core/modules/config/lib/Drupal/config/Tests/ConfigDependencyTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigDependencyTest.php @@ -2,7 +2,7 @@ /** * @file - * Contains Drupal\config\Tests\ConfigDependencyTest. + * Contains \Drupal\config\Tests\ConfigDependencyTest. */ namespace Drupal\config\Tests; diff -u b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php --- b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php +++ b/core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php @@ -156,7 +156,8 @@ $bundle_entity_type_id = $target_entity_type->getBundleEntityType(); if ($bundle_entity_type_id != 'bundle') { - // If the target entity type uses bundles then depend on the bundle entity. + // If the target entity type uses entities to manage its bundles then + // depend on the bundle entity. $bundle_entity = \Drupal::entityManager()->getStorageController($bundle_entity_type_id)->load($this->bundle); $this->addDependency('entity', $bundle_entity->getConfigDependencyName()); } diff -u b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php --- b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php +++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php @@ -286,7 +286,7 @@ $expected_dependencies = array( 'entity' => array('field.instance.node.article_rename.body', 'node.type.article_rename'), - 'module' => array('Core', 'text') + 'module' => array('text') ); // Check that the display has dependencies on the bundle, fields and the // modules that provide the formatters. diff -u b/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php b/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php --- b/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php +++ b/core/modules/field/lib/Drupal/field/Entity/FieldConfig.php @@ -279,8 +279,10 @@ else { $this->preSaveUpdated($storage_controller); } - // Ensure the correct dependencies are present. - $this->calculateDependencies(); + if (!$this->isSyncing()) { + // Ensure the correct dependencies are present. + $this->calculateDependencies(); + } } /** diff -u b/core/modules/field/lib/Drupal/field/Entity/FieldInstanceConfig.php b/core/modules/field/lib/Drupal/field/Entity/FieldInstanceConfig.php --- b/core/modules/field/lib/Drupal/field/Entity/FieldInstanceConfig.php +++ b/core/modules/field/lib/Drupal/field/Entity/FieldInstanceConfig.php @@ -355,8 +355,10 @@ // Notify the entity storage controller. $entity_manager->getStorageController($this->entity_type)->onInstanceUpdate($this); } - // Ensure the correct dependencies are present. - $this->calculateDependencies(); + if (!$this->isSyncing()) { + // Ensure the correct dependencies are present. + $this->calculateDependencies(); + } } /** diff -u b/core/modules/rdf/lib/Drupal/rdf/Entity/RdfMapping.php b/core/modules/rdf/lib/Drupal/rdf/Entity/RdfMapping.php --- b/core/modules/rdf/lib/Drupal/rdf/Entity/RdfMapping.php +++ b/core/modules/rdf/lib/Drupal/rdf/Entity/RdfMapping.php @@ -165,7 +165,8 @@ $this->addDependency('module', $entity_type->getProvider()); $bundle_entity_type_id = $entity_type->getBundleEntityType(); if ($bundle_entity_type_id != 'bundle') { - // If the target entity type uses bundles then depend on the bundle entity. + // If the target entity type uses entities to manage its bundles then + // depend on the bundle entity. $bundle_entity = \Drupal::entityManager()->getStorageController($bundle_entity_type_id)->load($this->bundle); $this->addDependency('entity', $bundle_entity->getConfigDependencyName()); } diff -u b/core/modules/views/lib/Drupal/views/Entity/View.php b/core/modules/views/lib/Drupal/views/Entity/View.php --- b/core/modules/views/lib/Drupal/views/Entity/View.php +++ b/core/modules/views/lib/Drupal/views/Entity/View.php @@ -22,7 +22,6 @@ * id = "view", * label = @Translation("View"), * controllers = { - * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController", * "access" = "Drupal\views\ViewAccessController" * }, * admin_permission = "administer views", diff -u b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php --- b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewStorageTest.php @@ -80,9 +80,6 @@ // Confirm that an info array has been returned. $this->assertTrue($this->entityType instanceof EntityTypeInterface, 'The View info array is loaded.'); - // Confirm we have the correct controller class. - $this->assertTrue($this->controller instanceof ConfigStorageController, 'The correct controller is loaded.'); - // CRUD tests. $this->loadTests(); $this->createTests(); diff -u b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php --- b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php @@ -111,6 +111,64 @@ } /** + * @covers ::preSave + */ + public function testPreSaveDuringSync() { + $query = $this->getMock('\Drupal\Core\Entity\Query\QueryInterface'); + $storage = $this->getMock('\Drupal\Core\Config\Entity\ConfigStorageControllerInterface'); + + $query->expects($this->any()) + ->method('execute') + ->will($this->returnValue(array())); + $query->expects($this->any()) + ->method('condition') + ->will($this->returnValue($query)); + $storage->expects($this->any()) + ->method('getQuery') + ->will($this->returnValue($query)); + $storage->expects($this->any()) + ->method('loadUnchanged') + ->will($this->returnValue($this->entity)); + + // Saving an entity will not reset the dependencies array during config + // synchronization. + $this->entity->set('dependencies', array('module' => array('node'))); + $this->entity->preSave($storage); + $this->assertEmpty($this->entity->get('dependencies')); + + $this->entity->setSyncing(TRUE); + $this->entity->set('dependencies', array('module' => array('node'))); + $this->entity->preSave($storage); + $dependencies = $this->entity->get('dependencies'); + $this->assertContains('node', $dependencies['module']); + } + + /** + * @covers ::addDependency + */ + public function testAddDependency() { + $method = new \ReflectionMethod('\Drupal\Core\Config\Entity\ConfigEntityBase', 'addDependency'); + $method->setAccessible(TRUE); + $method->invoke($this->entity, 'module', $this->provider); + $method->invoke($this->entity, 'module', 'Core'); + $method->invoke($this->entity, 'module', 'node'); + $dependencies = $this->entity->get('dependencies'); + $this->assertNotContains($this->provider, $dependencies['module']); + $this->assertNotContains('Core', $dependencies['module']); + $this->assertContains('node', $dependencies['module']); + + // Test sorting of dependencies. + $method->invoke($this->entity, 'module', 'action'); + $dependencies = $this->entity->get('dependencies'); + $this->assertEquals(array('action', 'node'), $dependencies['module']); + + // Test sorting of dependency types. + $method->invoke($this->entity, 'entity', 'system.action.id'); + $dependencies = $this->entity->get('dependencies'); + $this->assertEquals(array('entity', 'module'), array_keys($dependencies)); + } + + /** * @covers ::calculateDependencies */ public function testCalculateDependenciesWithPluginBag() {