Commit 2e7b2098 authored by catch's avatar catch

Issue #2030073 by alexpott: Config cannot be imported in order for dependencies.

parent ad293099
......@@ -6,6 +6,7 @@
*/
namespace Drupal\Core\Config;
use Drupal\Core\Config\Entity\ConfigDependencyManager;
/**
* Defines a config storage comparer.
......@@ -34,23 +35,33 @@ class StorageComparer implements StorageComparerInterface {
protected $changelist;
/**
* Lists all the configuration object names in the source storage.
*
* @see \Drupal\Core\Config\StorageComparer::getSourceNames()
* Sorted list of all the configuration object names in the source storage.
*
* @var array
*/
protected $sourceNames = array();
/**
* Lists all the configuration object names in the target storage.
*
* @see \Drupal\Core\Config\StorageComparer::getTargetNames()
* Sorted list of all the configuration object names in the target storage.
*
* @var array
*/
protected $targetNames = array();
/**
* The source configuration data keyed by name.
*
* @var array
*/
protected $sourceData = array();
/**
* The target configuration data keyed by name.
*
* @var array
*/
protected $targetData = array();
/**
* Constructs the Configuration storage comparer.
*
......@@ -101,51 +112,69 @@ public function getChangelist($op = NULL) {
}
/**
* {@inheritdoc}
* Adds changes to the changelist.
*
* @param string $op
* The change operation performed. Either delete, create or update.
* @param array $changes
* Array of changes to add to the changelist.
*/
public function addChangeList($op, array $changes) {
protected function addChangeList($op, array $changes) {
// Only add changes that aren't already listed.
$changes = array_diff($changes, $this->changelist[$op]);
$this->changelist[$op] = array_merge($this->changelist[$op], $changes);
return $this;
}
/**
* {@inheritdoc}
*/
public function createChangelist() {
return $this
->addChangelistCreate()
->addChangelistUpdate()
->addChangelistDelete();
$this->getAndSortConfigData();
$this->addChangelistCreate();
$this->addChangelistUpdate();
$this->addChangelistDelete();
$this->sourceData = NULL;
$this->targetData = NULL;
return $this;
}
/**
* {@inheritdoc}
* Creates the delete changelist.
*
* The list of deletes is sorted so that dependencies are deleted after
* configuration entities that depend on them. For example, field instances
* should be deleted after fields.
*/
public function addChangelistDelete() {
return $this->addChangeList('delete', array_diff($this->getTargetNames(), $this->getSourceNames()));
protected function addChangelistDelete() {
$deletes = array_diff(array_reverse($this->targetNames), $this->sourceNames);
$this->addChangeList('delete', $deletes);
}
/**
* {@inheritdoc}
* Creates the create changelist.
*
* The list of creates is sorted so that dependencies are created before
* configuration entities that depend on them. For example, fields
* should be created before field instances.
*/
public function addChangelistCreate() {
return $this->addChangeList('create', array_diff($this->getSourceNames(), $this->getTargetNames()));
protected function addChangelistCreate() {
$creates = array_diff($this->sourceNames, $this->targetNames);
$this->addChangeList('create', $creates);
}
/**
* {@inheritdoc}
* Creates the update changelist.
*
* The list of updates is sorted so that dependencies are created before
* configuration entities that depend on them. For example, fields
* should be updated before field instances.
*/
public function addChangelistUpdate() {
foreach (array_intersect($this->getSourceNames(), $this->getTargetNames()) as $name) {
$source_config_data = $this->sourceStorage->read($name);
$target_config_data = $this->targetStorage->read($name);
if ($source_config_data !== $target_config_data) {
protected function addChangelistUpdate() {
foreach (array_intersect($this->sourceNames, $this->targetNames) as $name) {
if ($this->sourceData[$name] !== $this->targetData[$name]) {
$this->addChangeList('update', array($name));
}
}
return $this;
}
/**
......@@ -169,32 +198,6 @@ public function hasChanges($ops = array('delete', 'create', 'update')) {
return FALSE;
}
/**
* Gets all the configuration names in the source storage.
*
* @return array
* List of all the configuration names in the source storage.
*/
protected function getSourceNames() {
if (empty($this->sourceNames)) {
$this->sourceNames = $this->sourceStorage->listAll();
}
return $this->sourceNames;
}
/**
* Gets all the configuration names in the target storage.
*
* @return array
* List of all the configuration names in the target storage.
*/
protected function getTargetNames() {
if (empty($this->targetNames)) {
$this->targetNames = $this->targetStorage->listAll();
}
return $this->targetNames;
}
/**
* {@inheritdoc}
*/
......@@ -204,4 +207,15 @@ public function validateSiteUuid() {
return $source['uuid'] === $target['uuid'];
}
/**
* Gets and sorts configuration data from the source and target storages.
*/
protected function getAndSortConfigData() {
$this->targetData = $this->targetStorage->readMultiple($this->targetStorage->listAll());
$this->sourceData = $this->sourceStorage->readMultiple($this->sourceStorage->listAll());
$dependency_manager = new ConfigDependencyManager();
$this->targetNames = $dependency_manager->setData($this->targetData)->sortAll();
$this->sourceNames = $dependency_manager->setData($this->sourceData)->sortAll();
}
}
......@@ -48,51 +48,6 @@ public function getEmptyChangelist();
*/
public function getChangelist($op = NULL);
/**
* Adds changes to the changelist.
*
* @param string $op
* The change operation performed. Either delete, create or update.
* @param array $changes
* Array of changes to add the changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function addChangeList($op, array $changes);
/**
* Add differences between source and target configuration storage to changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function createChangelist();
/**
* Creates the delete changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function addChangelistDelete();
/**
* Creates the create changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function addChangelistCreate();
/**
* Creates the update changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function addChangelistUpdate();
/**
* Recalculates the differences.
*
......
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Config\StorageComparerTest.
*/
namespace Drupal\Tests\Core\Config;
use Drupal\Component\Uuid\Php;
use Drupal\Core\Config\StorageComparer;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Config\StorageComparer
*
* @group Drupal
* @group Config
*/
class StorageComparerTest extends UnitTestCase {
/**
* @var \Drupal\Core\Config\StorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $sourceStorage;
/**
* @var \Drupal\Core\Config\StorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $targetStorage;
/**
* The storage comparer to test.
*
* @var \Drupal\Core\Config\StorageComparer
*/
protected $storageComparer;
/**
* An array of test configuration data keyed by configuration name.
*
* @var array
*/
protected $configData;
public static function getInfo() {
return array(
'description' => '',
'name' => '\Drupal\Core\Config\StorageComparer unit test',
'group' => 'Configuration',
);
}
public function setUp() {
$this->sourceStorage = $this->getMock('Drupal\Core\Config\StorageInterface');
$this->targetStorage = $this->getMock('Drupal\Core\Config\StorageInterface');
$this->storageComparer = new StorageComparer($this->sourceStorage, $this->targetStorage);
}
protected function getConfigData() {
$uuid = new Php();
// Mock data using minimal data to use ConfigDependencyManger.
$this->configData = array(
// Simple config that controls configuration sync.
'system.site' => array(
'title' => 'Drupal',
'uuid' => $uuid->generate(),
),
// Config entity which requires another config entity.
'field.instance.node.article.body' => array(
'id' => 'node.article.body',
'uuid' => $uuid->generate(),
'dependencies' => array(
'entity' => array(
'field.field.node.body'
),
),
),
// Config entity which is required by another config entity.
'field.field.node.body' => array(
'id' => 'node.body',
'uuid' => $uuid->generate(),
'dependencies' => array(
'module' => array(
'text',
),
),
),
// Config entity not which has no dependencies on configuration.
'views.view.test_view' => array(
'id' => 'test_view',
'uuid' => $uuid->generate(),
'dependencies' => array(
'module' => array(
'node',
),
),
),
// Simple config.
'system.performance' => array(
'stale_file_threshold' => 2592000
),
);
return $this->configData;
}
/**
* @covers ::createChangelist
*/
public function testCreateChangelistNoChange() {
$config_data = $this->getConfigData();
$config_files = array_keys($config_data);
$this->sourceStorage->expects($this->once())
->method('listAll')
->will($this->returnValue($config_files));
$this->targetStorage->expects($this->once())
->method('listAll')
->will($this->returnValue($config_files));
$this->sourceStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($config_data));
$this->targetStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($config_data));
$this->storageComparer->createChangelist();
$this->assertEmpty($this->storageComparer->getChangelist('create'));
$this->assertEmpty($this->storageComparer->getChangelist('delete'));
$this->assertEmpty($this->storageComparer->getChangelist('update'));
}
/**
* @covers ::createChangelist
*/
public function testCreateChangelistCreate() {
$target_data = $source_data = $this->getConfigData();
unset($target_data['field.field.node.body']);
unset($target_data['field.instance.node.article.body']);
unset($target_data['views.view.test_view']);
$this->sourceStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($source_data)));
$this->targetStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($target_data)));
$this->sourceStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($source_data));
$this->targetStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($target_data));
$this->storageComparer->createChangelist();
$expected = array(
'field.field.node.body',
'views.view.test_view',
'field.instance.node.article.body',
);
$this->assertEquals($expected, $this->storageComparer->getChangelist('create'));
$this->assertEmpty($this->storageComparer->getChangelist('delete'));
$this->assertEmpty($this->storageComparer->getChangelist('update'));
}
/**
* @covers ::createChangelist
*/
public function testCreateChangelistDelete() {
$target_data = $source_data = $this->getConfigData();
unset($source_data['field.field.node.body']);
unset($source_data['field.instance.node.article.body']);
unset($source_data['views.view.test_view']);
$this->sourceStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($source_data)));
$this->targetStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($target_data)));
$this->sourceStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($source_data));
$this->targetStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($target_data));
$this->storageComparer->createChangelist();
$expected = array(
'field.instance.node.article.body',
'views.view.test_view',
'field.field.node.body',
);
$this->assertEquals($expected, $this->storageComparer->getChangelist('delete'));
$this->assertEmpty($this->storageComparer->getChangelist('create'));
$this->assertEmpty($this->storageComparer->getChangelist('update'));
}
/**
* @covers ::createChangelist
*/
public function testCreateChangelistUpdate() {
$target_data = $source_data = $this->getConfigData();
$source_data['system.site']['title'] = 'Drupal New!';
$source_data['field.instance.node.article.body']['new_config_key'] = 'new data';
$source_data['field.field.node.body']['new_config_key'] = 'new data';
$this->sourceStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($source_data)));
$this->targetStorage->expects($this->once())
->method('listAll')
->will($this->returnValue(array_keys($target_data)));
$this->sourceStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($source_data));
$this->targetStorage->expects($this->once())
->method('readMultiple')
->will($this->returnValue($target_data));
$this->storageComparer->createChangelist();
$expected = array(
'field.field.node.body',
'system.site',
'field.instance.node.article.body',
);
$this->assertEquals($expected, $this->storageComparer->getChangelist('update'));
$this->assertEmpty($this->storageComparer->getChangelist('create'));
$this->assertEmpty($this->storageComparer->getChangelist('delete'));
}
}
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