Issue #2361423 by alexpott, larowlan, dixon_, swentel: Add a step to config import to allow contrib to create content based on config dependencies
parent
7a72d33bbc
commit
66c9f700d9
|
@ -234,6 +234,10 @@ services:
|
||||||
- { name: event_subscriber }
|
- { name: event_subscriber }
|
||||||
- { name: service_collector, tag: 'config.factory.override', call: addOverride }
|
- { name: service_collector, tag: 'config.factory.override', call: addOverride }
|
||||||
arguments: ['@config.storage', '@event_dispatcher', '@config.typed']
|
arguments: ['@config.storage', '@event_dispatcher', '@config.typed']
|
||||||
|
config.importer_subscriber:
|
||||||
|
class: Drupal\Core\Config\Importer\FinalMissingContentSubscriber
|
||||||
|
tags:
|
||||||
|
- { name: event_subscriber }
|
||||||
config.installer:
|
config.installer:
|
||||||
class: Drupal\Core\Config\ConfigInstaller
|
class: Drupal\Core\Config\ConfigInstaller
|
||||||
arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher']
|
arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher']
|
||||||
|
|
|
@ -98,6 +98,23 @@ final class ConfigEvents {
|
||||||
*/
|
*/
|
||||||
const IMPORT = 'config.importer.import';
|
const IMPORT = 'config.importer.import';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of event fired when missing content dependencies are detected.
|
||||||
|
*
|
||||||
|
* Events subscribers are fired as part of the configuration import batch.
|
||||||
|
* Each subscribe should call
|
||||||
|
* \Drupal\Core\Config\MissingContentEvent::resolveMissingContent() when they
|
||||||
|
* address a missing dependency. To address large amounts of dependencies
|
||||||
|
* subscribers can call
|
||||||
|
* \Drupal\Core\Config\MissingContentEvent::stopPropagation() which will stop
|
||||||
|
* calling other events and guarantee that the configuration import batch will
|
||||||
|
* fire the event again to continue processing missing content dependencies.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Config\ConfigImporter::processMissingContent()
|
||||||
|
* @see \Drupal\Core\Config\MissingContentEvent
|
||||||
|
*/
|
||||||
|
const IMPORT_MISSING_CONTENT = 'config.importer.missing_content';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of event fired to collect information on all config collections.
|
* Name of event fired to collect information on all config collections.
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
namespace Drupal\Core\Config;
|
namespace Drupal\Core\Config;
|
||||||
|
|
||||||
|
use Drupal\Core\Config\Importer\MissingContentEvent;
|
||||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||||
use Drupal\Core\Extension\ModuleInstallerInterface;
|
use Drupal\Core\Extension\ModuleInstallerInterface;
|
||||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||||
|
@ -536,7 +537,7 @@ class ConfigImporter {
|
||||||
$sync_steps[] = 'processExtensions';
|
$sync_steps[] = 'processExtensions';
|
||||||
}
|
}
|
||||||
$sync_steps[] = 'processConfigurations';
|
$sync_steps[] = 'processConfigurations';
|
||||||
|
$sync_steps[] = 'processMissingContent';
|
||||||
// Allow modules to add new steps to configuration synchronization.
|
// Allow modules to add new steps to configuration synchronization.
|
||||||
$this->moduleHandler->alter('config_import_steps', $sync_steps, $this);
|
$this->moduleHandler->alter('config_import_steps', $sync_steps, $this);
|
||||||
$sync_steps[] = 'finish';
|
$sync_steps[] = 'finish';
|
||||||
|
@ -606,6 +607,38 @@ class ConfigImporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles processing of missing content.
|
||||||
|
*
|
||||||
|
* @param array $context
|
||||||
|
* Standard batch context.
|
||||||
|
*/
|
||||||
|
protected function processMissingContent(array &$context) {
|
||||||
|
$sandbox = &$context['sandbox']['config'];
|
||||||
|
if (!isset($sandbox['missing_content'])) {
|
||||||
|
$missing_content = $this->configManager->findMissingContentDependencies();
|
||||||
|
$sandbox['missing_content']['data'] = $missing_content;
|
||||||
|
$sandbox['missing_content']['total'] = count($missing_content);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$missing_content = $sandbox['missing_content']['data'];
|
||||||
|
}
|
||||||
|
if (!empty($missing_content)) {
|
||||||
|
$event = new MissingContentEvent($missing_content);
|
||||||
|
// Fire an event to allow listeners to create the missing content.
|
||||||
|
$this->eventDispatcher->dispatch(ConfigEvents::IMPORT_MISSING_CONTENT, $event);
|
||||||
|
$sandbox['missing_content']['data'] = $event->getMissingContent();
|
||||||
|
}
|
||||||
|
$current_count = count($sandbox['missing_content']['data']);
|
||||||
|
if ($current_count) {
|
||||||
|
$context['message'] = $this->t('Resolving missing content');
|
||||||
|
$context['finished'] = ($sandbox['missing_content']['total'] - $current_count) / $sandbox['missing_content']['total'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$context['finished'] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finishes the batch.
|
* Finishes the batch.
|
||||||
*
|
*
|
||||||
|
|
|
@ -447,4 +447,29 @@ class ConfigManager implements ConfigManagerInterface {
|
||||||
return $entity->onDependencyRemoval($affected_dependencies);
|
return $entity->onDependencyRemoval($affected_dependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function findMissingContentDependencies() {
|
||||||
|
$content_dependencies = array();
|
||||||
|
$missing_dependencies = array();
|
||||||
|
foreach ($this->activeStorage->readMultiple($this->activeStorage->listAll()) as $config_data) {
|
||||||
|
if (isset($config_data['dependencies']['content'])) {
|
||||||
|
$content_dependencies = array_merge($content_dependencies, $config_data['dependencies']['content']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (array_unique($content_dependencies) as $content_dependency) {
|
||||||
|
// Format of the dependency is entity_type:bundle:uuid.
|
||||||
|
list($entity_type, $bundle, $uuid) = explode(':', $content_dependency, 3);
|
||||||
|
if (!$this->entityManager->loadEntityByUuid($entity_type, $uuid)) {
|
||||||
|
$missing_dependencies[$uuid] = array(
|
||||||
|
'entity_type' => $entity_type,
|
||||||
|
'bundle' => $bundle,
|
||||||
|
'uuid' => $uuid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $missing_dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,4 +180,14 @@ interface ConfigManagerInterface {
|
||||||
*/
|
*/
|
||||||
public function getConfigCollectionInfo();
|
public function getConfigCollectionInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds missing content dependencies declared in configuration entities.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* A list of missing content dependencies. The array is keyed by UUID. Each
|
||||||
|
* value is an array with the following keys: 'entity_type', 'bundle' and
|
||||||
|
* 'uuid'.
|
||||||
|
*/
|
||||||
|
public function findMissingContentDependencies();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\Core\Config\Importer\FinalMissingContentSubscriber.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\Config\Importer;
|
||||||
|
|
||||||
|
use Drupal\Core\Config\ConfigEvents;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final event subscriber to the missing content event.
|
||||||
|
*
|
||||||
|
* Ensure that all missing content dependencies are removed from the event so
|
||||||
|
* the importer can complete.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Config\ConfigImporter::processMissingContent()
|
||||||
|
*/
|
||||||
|
class FinalMissingContentSubscriber implements EventSubscriberInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the missing content event.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Config\Importer\MissingContentEvent $event
|
||||||
|
* The missing content event.
|
||||||
|
*/
|
||||||
|
public function onMissingContent(MissingContentEvent $event) {
|
||||||
|
foreach (array_keys($event->getMissingContent()) as $uuid) {
|
||||||
|
$event->resolveMissingContent($uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function getSubscribedEvents() {
|
||||||
|
// This should always be the final event as it will mark all content
|
||||||
|
// dependencies as resolved.
|
||||||
|
$events[ConfigEvents::IMPORT_MISSING_CONTENT][] = array('onMissingContent', -1024);
|
||||||
|
return $events;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\Core\Config\MissingContentEvent.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\Core\Config\Importer;
|
||||||
|
|
||||||
|
use Symfony\Component\EventDispatcher\Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a configuration event for event listeners.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Config\Config\ConfigEvents::IMPORT_MISSING_CONTENT
|
||||||
|
*/
|
||||||
|
class MissingContentEvent extends Event {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of missing content dependencies.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $missingContent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a configuration import missing content event object.
|
||||||
|
*
|
||||||
|
* @param array $missing_content
|
||||||
|
* Missing content information.
|
||||||
|
*/
|
||||||
|
public function __construct(array $missing_content) {
|
||||||
|
$this->missingContent = $missing_content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets missing content information.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* A list of missing content dependencies. The array is keyed by UUID. Each
|
||||||
|
* value is an array with the following keys: 'entity_type', 'bundle' and
|
||||||
|
* 'uuid'.
|
||||||
|
*/
|
||||||
|
public function getMissingContent() {
|
||||||
|
return $this->missingContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the missing content by removing it from the list.
|
||||||
|
*
|
||||||
|
* @param string $uuid
|
||||||
|
* The UUID of the content entity to mark resolved.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
* The MissingContentEvent object.
|
||||||
|
*/
|
||||||
|
public function resolveMissingContent($uuid) {
|
||||||
|
if (isset($this->missingContent[$uuid])) {
|
||||||
|
unset($this->missingContent[$uuid]);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -8,21 +8,23 @@
|
||||||
namespace Drupal\config\Tests;
|
namespace Drupal\config\Tests;
|
||||||
|
|
||||||
use Drupal\entity_test\Entity\EntityTest;
|
use Drupal\entity_test\Entity\EntityTest;
|
||||||
use Drupal\simpletest\KernelTestBase;
|
use Drupal\system\Tests\Entity\EntityUnitTestBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for configuration dependencies.
|
* Tests for configuration dependencies.
|
||||||
*
|
*
|
||||||
* @group config
|
* @group config
|
||||||
*/
|
*/
|
||||||
class ConfigDependencyTest extends KernelTestBase {
|
class ConfigDependencyTest extends EntityUnitTestBase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modules to enable.
|
* Modules to enable.
|
||||||
*
|
*
|
||||||
|
* The entity_test module is enabled to provide content entity types.
|
||||||
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public static $modules = array('system', 'config_test', 'entity_test', 'user');
|
public static $modules = array('config_test', 'entity_test', 'user');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that calculating dependencies for system module.
|
* Tests that calculating dependencies for system module.
|
||||||
|
@ -41,7 +43,8 @@ class ConfigDependencyTest extends KernelTestBase {
|
||||||
/**
|
/**
|
||||||
* Tests creating dependencies on configuration entities.
|
* Tests creating dependencies on configuration entities.
|
||||||
*/
|
*/
|
||||||
public function testDependencyMangement() {
|
public function testDependencyManagement() {
|
||||||
|
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||||
$config_manager = \Drupal::service('config.manager');
|
$config_manager = \Drupal::service('config.manager');
|
||||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||||
// Test dependencies between modules.
|
// Test dependencies between modules.
|
||||||
|
@ -110,9 +113,14 @@ class ConfigDependencyTest extends KernelTestBase {
|
||||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.');
|
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.');
|
||||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.');
|
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.');
|
||||||
|
|
||||||
// Test dependency on a fake content entity.
|
// Test dependency on a content entity.
|
||||||
$entity2->setEnforcedDependencies(['config' => [$entity1->getConfigDependencyName()], 'content' => ['node:page:uuid']])->save();;
|
$entity_test = entity_create('entity_test', array(
|
||||||
$dependents = $config_manager->findConfigEntityDependents('content', array('node:page:uuid'));
|
'name' => $this->randomString(),
|
||||||
|
'type' => 'entity_test',
|
||||||
|
));
|
||||||
|
$entity_test->save();
|
||||||
|
$entity2->setEnforcedDependencies(['config' => [$entity1->getConfigDependencyName()], 'content' => [$entity_test->getConfigDependencyName()]])->save();;
|
||||||
|
$dependents = $config_manager->findConfigEntityDependents('content', array($entity_test->getConfigDependencyName()));
|
||||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the content entity.');
|
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the content entity.');
|
||||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the content entity.');
|
$this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the content entity.');
|
||||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the content entity (via entity2).');
|
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the content entity (via entity2).');
|
||||||
|
@ -151,6 +159,30 @@ class ConfigDependencyTest extends KernelTestBase {
|
||||||
$this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 has a dependency on config_test module.');
|
$this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 has a dependency on config_test module.');
|
||||||
$this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on config_test module.');
|
$this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on config_test module.');
|
||||||
|
|
||||||
|
// Test the ability to find missing content dependencies.
|
||||||
|
$missing_dependencies = $config_manager->findMissingContentDependencies();
|
||||||
|
$this->assertEqual([], $missing_dependencies);
|
||||||
|
|
||||||
|
$expected = [$entity_test->uuid() => [
|
||||||
|
'entity_type' => 'entity_test',
|
||||||
|
'bundle' => $entity_test->bundle(),
|
||||||
|
'uuid' => $entity_test->uuid(),
|
||||||
|
]];
|
||||||
|
// Delete the content entity so that is it now missing.
|
||||||
|
$entity_test->delete();
|
||||||
|
$missing_dependencies = $config_manager->findMissingContentDependencies();
|
||||||
|
$this->assertEqual($expected, $missing_dependencies);
|
||||||
|
|
||||||
|
// Add a fake missing dependency to ensure multiple missing dependencies
|
||||||
|
// work.
|
||||||
|
$entity1->setEnforcedDependencies(['content' => [$entity_test->getConfigDependencyName(), 'entity_test:bundle:uuid']])->save();;
|
||||||
|
$expected['uuid'] = [
|
||||||
|
'entity_type' => 'entity_test',
|
||||||
|
'bundle' => 'bundle',
|
||||||
|
'uuid' => 'uuid',
|
||||||
|
];
|
||||||
|
$missing_dependencies = $config_manager->findMissingContentDependencies();
|
||||||
|
$this->assertEqual($expected, $missing_dependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Contains \Drupal\config\Tests\ConfigImporterMissingContentTest.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Drupal\config\Tests;
|
||||||
|
|
||||||
|
use Drupal\Component\Utility\SafeMarkup;
|
||||||
|
use Drupal\Core\Config\ConfigImporter;
|
||||||
|
use Drupal\Core\Config\ConfigImporterException;
|
||||||
|
use Drupal\Core\Config\StorageComparer;
|
||||||
|
use Drupal\simpletest\KernelTestBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests importing configuration which has missing content dependencies.
|
||||||
|
*
|
||||||
|
* @group config
|
||||||
|
*/
|
||||||
|
class ConfigImporterMissingContentTest extends KernelTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config Importer object used for testing.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Config\ConfigImporter
|
||||||
|
*/
|
||||||
|
protected $configImporter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modules to enable.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public static $modules = array('system', 'user', 'entity_test', 'config_test', 'config_import_test');
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$this->installSchema('system', 'sequences');
|
||||||
|
$this->installEntitySchema('entity_test');
|
||||||
|
$this->installEntitySchema('user');
|
||||||
|
$this->installConfig(array('config_test'));
|
||||||
|
// Installing config_test's default configuration pollutes the global
|
||||||
|
// variable being used for recording hook invocations by this test already,
|
||||||
|
// so it has to be cleared out manually.
|
||||||
|
unset($GLOBALS['hook_config_test']);
|
||||||
|
|
||||||
|
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||||
|
|
||||||
|
// Set up the ConfigImporter object for testing.
|
||||||
|
$storage_comparer = new StorageComparer(
|
||||||
|
$this->container->get('config.storage.staging'),
|
||||||
|
$this->container->get('config.storage'),
|
||||||
|
$this->container->get('config.manager')
|
||||||
|
);
|
||||||
|
$this->configImporter = new ConfigImporter(
|
||||||
|
$storage_comparer->createChangelist(),
|
||||||
|
$this->container->get('event_dispatcher'),
|
||||||
|
$this->container->get('config.manager'),
|
||||||
|
$this->container->get('lock'),
|
||||||
|
$this->container->get('config.typed'),
|
||||||
|
$this->container->get('module_handler'),
|
||||||
|
$this->container->get('module_installer'),
|
||||||
|
$this->container->get('theme_handler'),
|
||||||
|
$this->container->get('string_translation')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the missing content event is fired.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Config\ConfigImporter::processMissingContent()
|
||||||
|
* @see \Drupal\config_import_test\EventSubscriber
|
||||||
|
*/
|
||||||
|
function testMissingContent() {
|
||||||
|
\Drupal::state()->set('config_import_test.config_import_missing_content', TRUE);
|
||||||
|
|
||||||
|
// Update a configuration entity in the staging directory to have a
|
||||||
|
// dependency on two content entities that do not exist.
|
||||||
|
$storage = $this->container->get('config.storage');
|
||||||
|
$staging = $this->container->get('config.storage.staging');
|
||||||
|
$entity_one = entity_create('entity_test', array('name' => 'one'));
|
||||||
|
$entity_two = entity_create('entity_test', array('name' => 'two'));
|
||||||
|
$entity_three = entity_create('entity_test', array('name' => 'three'));
|
||||||
|
$dynamic_name = 'config_test.dynamic.dotted.default';
|
||||||
|
$original_dynamic_data = $storage->read($dynamic_name);
|
||||||
|
// Entity one will be resolved by
|
||||||
|
// \Drupal\config_import_test\EventSubscriber::onConfigImporterMissingContentOne().
|
||||||
|
$original_dynamic_data['dependencies']['content'][] = $entity_one->getConfigDependencyName();
|
||||||
|
// Entity two will be resolved by
|
||||||
|
// \Drupal\config_import_test\EventSubscriber::onConfigImporterMissingContentTwo().
|
||||||
|
$original_dynamic_data['dependencies']['content'][] = $entity_two->getConfigDependencyName();
|
||||||
|
// Entity three will be resolved by
|
||||||
|
// \Drupal\Core\Config\Importer\FinalMissingContentSubscriber.
|
||||||
|
$original_dynamic_data['dependencies']['content'][] = $entity_three->getConfigDependencyName();
|
||||||
|
$staging->write($dynamic_name, $original_dynamic_data);
|
||||||
|
|
||||||
|
// Import.
|
||||||
|
$this->configImporter->reset()->import();
|
||||||
|
$this->assertEqual([], $this->configImporter->getErrors(), 'There were no errors during the import.');
|
||||||
|
$this->assertEqual($entity_one->uuid(), \Drupal::state()->get('config_import_test.config_import_missing_content_one'), 'The missing content event is fired during configuration import.');
|
||||||
|
$this->assertEqual($entity_two->uuid(), \Drupal::state()->get('config_import_test.config_import_missing_content_two'), 'The missing content event is fired during configuration import.');
|
||||||
|
$original_dynamic_data = $storage->read($dynamic_name);
|
||||||
|
$this->assertEqual([$entity_one->getConfigDependencyName(), $entity_two->getConfigDependencyName(), $entity_three->getConfigDependencyName()], $original_dynamic_data['dependencies']['content'], 'The imported configuration entity has the missing content entity dependency.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ namespace Drupal\config_import_test;
|
||||||
use Drupal\Core\Config\ConfigCrudEvent;
|
use Drupal\Core\Config\ConfigCrudEvent;
|
||||||
use Drupal\Core\Config\ConfigEvents;
|
use Drupal\Core\Config\ConfigEvents;
|
||||||
use Drupal\Core\Config\ConfigImporterEvent;
|
use Drupal\Core\Config\ConfigImporterEvent;
|
||||||
|
use Drupal\Core\Config\Importer\MissingContentEvent;
|
||||||
use Drupal\Core\State\StateInterface;
|
use Drupal\Core\State\StateInterface;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
|
||||||
|
@ -51,6 +52,39 @@ class EventSubscriber implements EventSubscriberInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the missing content event.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Config\Importer\MissingContentEvent $event
|
||||||
|
* The missing content event.
|
||||||
|
*/
|
||||||
|
public function onConfigImporterMissingContentOne(MissingContentEvent $event) {
|
||||||
|
if ($this->state->get('config_import_test.config_import_missing_content', FALSE) && $this->state->get('config_import_test.config_import_missing_content_one', FALSE) === FALSE) {
|
||||||
|
$missing = $event->getMissingContent();
|
||||||
|
$uuid = key($missing);
|
||||||
|
$this->state->set('config_import_test.config_import_missing_content_one', key($missing));
|
||||||
|
$event->resolveMissingContent($uuid);
|
||||||
|
// Stopping propagation ensures that onConfigImporterMissingContentTwo
|
||||||
|
// will be fired on the next batch step.
|
||||||
|
$event->stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the missing content event.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Config\Importer\MissingContentEvent $event
|
||||||
|
* The missing content event.
|
||||||
|
*/
|
||||||
|
public function onConfigImporterMissingContentTwo(MissingContentEvent $event) {
|
||||||
|
if ($this->state->get('config_import_test.config_import_missing_content', FALSE) && $this->state->get('config_import_test.config_import_missing_content_two', FALSE) === FALSE) {
|
||||||
|
$missing = $event->getMissingContent();
|
||||||
|
$uuid = key($missing);
|
||||||
|
$this->state->set('config_import_test.config_import_missing_content_two', key($missing));
|
||||||
|
$event->resolveMissingContent($uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reacts to a config save and records information in state for testing.
|
* Reacts to a config save and records information in state for testing.
|
||||||
*
|
*
|
||||||
|
@ -106,6 +140,7 @@ class EventSubscriber implements EventSubscriberInterface {
|
||||||
$events[ConfigEvents::SAVE][] = array('onConfigSave', 40);
|
$events[ConfigEvents::SAVE][] = array('onConfigSave', 40);
|
||||||
$events[ConfigEvents::DELETE][] = array('onConfigDelete', 40);
|
$events[ConfigEvents::DELETE][] = array('onConfigDelete', 40);
|
||||||
$events[ConfigEvents::IMPORT_VALIDATE] = array('onConfigImporterValidate');
|
$events[ConfigEvents::IMPORT_VALIDATE] = array('onConfigImporterValidate');
|
||||||
|
$events[ConfigEvents::IMPORT_MISSING_CONTENT] = array(array('onConfigImporterMissingContentOne'), array('onConfigImporterMissingContentTwo', -100));
|
||||||
return $events;
|
return $events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue