diff --git a/core/lib/Drupal/Core/Config/ConfigManager.php b/core/lib/Drupal/Core/Config/ConfigManager.php index 49f406d..3d2e9b4 100644 --- a/core/lib/Drupal/Core/Config/ConfigManager.php +++ b/core/lib/Drupal/Core/Config/ConfigManager.php @@ -12,6 +12,7 @@ use Drupal\Core\Config\Entity\ConfigDependencyManager; use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; @@ -312,16 +313,12 @@ public function getConfigEntitiesToChange($operation, $type, array $names, $dry_ $original_dependencies = $dependents; $update_uuids = []; - $return = [ - 'update' => [], - 'delete' => [], - 'unchanged' => [], - ]; + $return = ['update' => [], 'delete' => [], 'unchanged' => []]; // Try to fix any dependencies and find out what will happen to the // dependency graph. foreach ($dependents as $dependent) { - /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $dependent */ + /** @var \Drupal\Core\Config\Entity\ConfigEntityBase $dependent */ if ($dry_run) { // Clone the entity so any changes do not change any static caches. $dependent = clone $dependent; @@ -346,6 +343,8 @@ public function getConfigEntitiesToChange($operation, $type, array $names, $dry_ $return['update'][] = $dependent; $update_uuids[] = $dependent->uuid(); } + // Clear the 'dependency_resolved' flag. + $dependent->setDependencyResolved(FALSE); } } @@ -360,6 +359,11 @@ public function getConfigEntitiesToChange($operation, $type, array $names, $dry_ $delete_uuids = array_map(function ($dependent) { return $dependent->uuid(); }, $return['delete']); + // Fix back the $return['update'] list. An entity marked for update can be + // also in $return['delete'] list. The removal take precedence. + $return['update'] = array_filter($return['update'], function(ConfigEntityInterface $entity) use ($delete_uuids) { + return !in_array($entity->uuid(), $delete_uuids); + }); } else { // All other actions will leave the entities unchanged. @@ -442,7 +446,7 @@ protected function callOnDependencyResolver($operation, ConfigEntityInterface $e } // Inform the entity. - $method = $operation === ConfigManagerInterface::REMOVE ? 'onDependencyRemoval' : 'onDependencyUpdate'; + $method = $operation === ConfigManagerInterface::REMOVE ? 'onDependencyRemoval' : 'onDependencyUpdating'; $changed = $entity->$method($affected_dependencies); /** @var \Drupal\Core\Config\Entity\ConfigEntityDependencyResolverInterface $entity */ $entity->setDependencyResolved($changed); @@ -487,38 +491,36 @@ public function findMissingContentDependencies() { * @param string $type * The type of dependency. Either 'module', 'theme', 'config' or 'content'. * @param array $names - * A list of dependency names to be normalized If $type equals 'module' or + * A list of dependency names to be normalized. If $type equals 'module' or * 'theme' then it should be a list of module names or theme names. In the * case of 'config' or 'content' it should be a list of configuration - * dependency names. When type equals 'config' or 'content', the caller can - * pass the names as array keys and the full entity objects as values. + * dependency names. For entity dependencies, the caller can, alternatively, + * pass the full entity object instead of its dependency name. */ protected function normalizeNames($type, array &$names) { + $normalized = []; foreach ($names as $key => $value) { // The name has been pass as string, in the value. if (is_string($value)) { if ($type == 'config') { - $names[$value] = $this->loadConfigEntityByName($value); + $normalized[$value] = $this->loadConfigEntityByName($value); } elseif ($type == 'content') { // Ignore the bundle. list($entity_type_id, , $uuid) = explode(':', $value); - $names[$value] = $this->entityManager->loadEntityByConfigTarget($entity_type_id, $uuid); + $normalized[$value] = $this->entityManager->loadEntityByConfigTarget($entity_type_id, $uuid); } else { - $names[$value] = $value; + $normalized[$value] = $value; } - unset($names[$key]); } - // The name has been passed as key and the configuration entity as value. - elseif ($value instanceof ConfigEntityInterface && is_string($key)) { - // Nothing needs to be done. - } - // Malformed item. - else { - unset($names[$key]); + // The name has been passed as a full config or content entity. + elseif ($value instanceof EntityInterface) { + /** @var \Drupal\Core\Entity\EntityInterface $value */ + $normalized[$value->getConfigDependencyName()] = $value; } } + $names = $normalized; } } diff --git a/core/lib/Drupal/Core/Config/ConfigManagerDependencyFinderInterface.php b/core/lib/Drupal/Core/Config/ConfigManagerDependencyFinderInterface.php index 9882882..34e1cf8 100644 --- a/core/lib/Drupal/Core/Config/ConfigManagerDependencyFinderInterface.php +++ b/core/lib/Drupal/Core/Config/ConfigManagerDependencyFinderInterface.php @@ -27,7 +27,9 @@ * @param array $names * The specific names to check. If $type equals 'module' or 'theme' then it * should be a list of module names or theme names. In the case of 'config' - * or 'content' it should be a list of configuration dependency names. + * or 'content' it should be a list of configuration dependency names. For + * entity dependencies, the caller can, alternatively, pass the full entity + * object instead of its dependency name. * @param bool $dry_run * If set to FALSE the entities returned in the list of updates will be * modified. In order to make the changes the caller needs to save them. If diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php index 431d0b3..bbd0d1d 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -356,20 +356,19 @@ public function preSave(EntityStorageInterface $storage) { if ($original && ($original->uuid() != $this->uuid())) { throw new ConfigDuplicateUUIDException("Attempt to save a configuration entity '{$this->id()}' with UUID '{$this->uuid()}' when this entity already exists with UUID '{$original->uuid()}'"); } + + // Fix any dependencies. + $config_entities = $this->getConfigManager() ->getConfigEntitiesToChange(ConfigManagerInterface::UPDATE, 'config', [$this], FALSE); + /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $dependent_entity */ + foreach ($config_entities['update'] as $dependent_entity) { + $dependent_entity->save(); + } } if (!$this->isSyncing()) { // Ensure the correct dependencies are present. If the configuration is // being written during a configuration synchronization then there is no // need to recalculate the dependencies. $this->calculateDependencies(); - // Fix any dependencies. - $config_entities = $this->getConfigManager()->getConfigEntitiesToChange(ConfigManagerInterface::UPDATE, 'config', [$this->getConfigDependencyName() => $this], FALSE); - if (!empty($config_entities['update'])) { - /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $dependent_entity */ - foreach ($config_entities['update'] as $dependent_entity) { - $dependent_entity->save(); - } - } } } @@ -510,7 +509,7 @@ public function onDependencyRemoval(array $dependencies) { /** * {@inheritdoc} */ - public function onDependencyUpdate(array $dependencies) { + public function onDependencyUpdating(array $dependencies) { return FALSE; } @@ -594,7 +593,7 @@ public static function preDelete(EntityStorageInterface $storage, array $entitie break; } // Fix or remove any dependencies. - $config_entities = static::getConfigManager()->getConfigEntitiesToChangeOnDependencyRemoval('config', [$entity->getConfigDependencyName()], FALSE); + $config_entities = static::getConfigManager()->getConfigEntitiesToChangeOnDependencyRemoval('config', [$entity->getConfigDependencyName()]); /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $dependent_entity */ foreach ($config_entities['update'] as $dependent_entity) { $dependent_entity->save(); diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityDependencyResolverInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityDependencyResolverInterface.php index 533244c..5dd7f16 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityDependencyResolverInterface.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityDependencyResolverInterface.php @@ -31,7 +31,7 @@ * @see \Drupal\Core\Config\Entity\ConfigDependencyManager * @see \Drupal\Core\Config\ConfigEntityBase::preSave() */ - public function onDependencyUpdate(array $dependencies); + public function onDependencyUpdating(array $dependencies); /** * Sets or removes the 'dependency_resolved' flag. diff --git a/core/modules/editor/src/Entity/Editor.php b/core/modules/editor/src/Entity/Editor.php index fa5d254..4777280 100644 --- a/core/modules/editor/src/Entity/Editor.php +++ b/core/modules/editor/src/Entity/Editor.php @@ -188,8 +188,8 @@ public function setImageUploadSettings(array $image_upload_settings) { /** * {@inheritdoc} */ - public function onDependencyUpdate(array $dependencies) { - $changed = parent::onDependencyUpdate($dependencies); + public function onDependencyUpdating(array $dependencies) { + $changed = parent::onDependencyUpdating($dependencies); if (!empty($format = $dependencies['config'][$this->getFilterFormat()->getConfigDependencyName()])) { if (($status = $format->status()) !== $format->original->status()) { diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php index 9e8766c..44b2d62 100644 --- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php @@ -134,6 +134,8 @@ protected function setUp() { $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator); $container->set('config.typed', $this->typedConfigManager); $config_manager = $this->getMock(ConfigManagerDependencyFinderInterface::class); + $config_manager->method('getConfigEntitiesToChange') + ->will($this->returnValue(['update' => []]));; $container->set('config.manager', $config_manager); \Drupal::setContainer($container); diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php index ecb3788..5a7df26 100644 --- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php @@ -180,6 +180,8 @@ protected function setUp() { $this->configManager = $this->getMockBuilder(ConfigManager::class) ->disableOriginalConstructor() ->getMock(); + $this->configManager->method('getConfigEntitiesToChange') + ->will($this->returnValue(['update' => []])); $this->cacheContextsManager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager') ->disableOriginalConstructor()