Issue #2310093 by alexpott, hussainweb : Fixed Config install and import should map from storage record not set properties directly.
parent
0803312d9a
commit
ac9054bed0
|
@ -225,16 +225,12 @@ class ConfigInstaller implements ConfigInstallerInterface {
|
|||
if ($this->getActiveStorage($collection)->exists($name)) {
|
||||
$id = $entity_storage->getIDFromConfigName($name, $entity_storage->getEntityType()->getConfigPrefix());
|
||||
$entity = $entity_storage->load($id);
|
||||
foreach ($new_config->get() as $property => $value) {
|
||||
$entity->set($property, $value);
|
||||
}
|
||||
$entity->save();
|
||||
$entity = $entity_storage->updateFromStorageRecord($entity, $new_config->get());
|
||||
}
|
||||
else {
|
||||
$entity_storage
|
||||
->create($new_config->get())
|
||||
->save();
|
||||
$entity = $entity_storage->createFromStorageRecord($new_config->get());
|
||||
}
|
||||
$entity->save();
|
||||
}
|
||||
else {
|
||||
$new_config->save();
|
||||
|
|
|
@ -347,7 +347,7 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function importCreate($name, Config $new_config, Config $old_config) {
|
||||
$entity = $this->create($new_config->get());
|
||||
$entity = $this->createFromStorageRecord($new_config->get());
|
||||
$entity->setSyncing(TRUE);
|
||||
$entity->save();
|
||||
return TRUE;
|
||||
|
@ -363,16 +363,7 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
|
|||
throw new ConfigImporterException(String::format('Attempt to update non-existing entity "@id".', array('@id' => $id)));
|
||||
}
|
||||
$entity->setSyncing(TRUE);
|
||||
$entity->original = clone $entity;
|
||||
|
||||
foreach ($old_config->get() as $property => $value) {
|
||||
$entity->original->set($property, $value);
|
||||
}
|
||||
|
||||
foreach ($new_config->get() as $property => $value) {
|
||||
$entity->set($property, $value);
|
||||
}
|
||||
|
||||
$entity = $this->updateFromStorageRecord($entity, $new_config->get());
|
||||
$entity->save();
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -392,15 +383,44 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function importRename($old_name, Config $new_config, Config $old_config) {
|
||||
$id = static::getIDFromConfigName($old_name, $this->entityType->getConfigPrefix());
|
||||
$entity = $this->load($id);
|
||||
$entity->setSyncing(TRUE);
|
||||
$data = $new_config->get();
|
||||
foreach ($data as $key => $value) {
|
||||
$entity->set($key, $value);
|
||||
return $this->importUpdate($old_name, $new_config, $old_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createFromStorageRecord(array $values) {
|
||||
// Assign a new UUID if there is none yet.
|
||||
if ($this->uuidKey && $this->uuidService && !isset($values[$this->uuidKey])) {
|
||||
$values[$this->uuidKey] = $this->uuidService->generate();
|
||||
}
|
||||
$entity->save();
|
||||
return TRUE;
|
||||
$data = $this->mapFromStorageRecords(array($values));
|
||||
$entity = current($data);
|
||||
$entity->original = clone $entity;
|
||||
$entity->enforceIsNew();
|
||||
$entity->postCreate($this);
|
||||
|
||||
// Modules might need to add or change the data initially held by the new
|
||||
// entity object, for instance to fill-in default values.
|
||||
$this->invokeHook('create', $entity);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateFromStorageRecord(ConfigEntityInterface $entity, array $values) {
|
||||
$entity->original = clone $entity;
|
||||
|
||||
$data = $this->mapFromStorageRecords(array($values));
|
||||
$updated_entity = current($data);
|
||||
|
||||
foreach (array_keys($values) as $property) {
|
||||
$value = $updated_entity->get($property);
|
||||
$entity->set($property, $value);
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,4 +29,40 @@ interface ConfigEntityStorageInterface extends EntityStorageInterface {
|
|||
*/
|
||||
public static function getIDFromConfigName($config_name, $config_prefix);
|
||||
|
||||
/**
|
||||
* Creates a configuration entity from storage values.
|
||||
*
|
||||
* Allows the configuration entity storage to massage storage values before
|
||||
* creating an entity.
|
||||
*
|
||||
* @param array $values
|
||||
* The array of values from the configuration storage.
|
||||
*
|
||||
* @return ConfigEntityInterface
|
||||
* The configuration entity.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityStorageBase::mapFromStorageRecords()
|
||||
* @see \Drupal\field\FieldStorageConfigStorage::mapFromStorageRecords()
|
||||
*/
|
||||
public function createFromStorageRecord(array $values);
|
||||
|
||||
/**
|
||||
* Updates a configuration entity from storage values.
|
||||
*
|
||||
* Allows the configuration entity storage to massage storage values before
|
||||
* updating an entity.
|
||||
*
|
||||
* @param ConfigEntityInterface $entity
|
||||
* The configuration entity to update.
|
||||
* @param array $values
|
||||
* The array of values from the configuration storage.
|
||||
*
|
||||
* @return ConfigEntityInterface
|
||||
* The configuration entity.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityStorageBase::mapFromStorageRecords()
|
||||
* @see \Drupal\field\FieldStorageConfigStorage::mapFromStorageRecords()
|
||||
*/
|
||||
public function updateFromStorageRecord(ConfigEntityInterface $entity, array $values);
|
||||
|
||||
}
|
||||
|
|
|
@ -94,7 +94,8 @@ trait SchemaCheckTrait {
|
|||
if ($element instanceof PrimitiveInterface) {
|
||||
$success =
|
||||
($type == 'integer' && $element instanceof IntegerInterface) ||
|
||||
($type == 'double' && $element instanceof FloatInterface) ||
|
||||
// Allow integer values in a float field.
|
||||
(($type == 'double' || $type == 'integer') && $element instanceof FloatInterface) ||
|
||||
($type == 'boolean' && $element instanceof BooleanInterface) ||
|
||||
($type == 'string' && $element instanceof StringInterface) ||
|
||||
// Null values are allowed for all types.
|
||||
|
|
|
@ -246,12 +246,16 @@ class ConfigSingleImportForm extends ConfirmFormBase {
|
|||
$this->config($this->data['config_name'])->setData($this->data['import'])->save();
|
||||
drupal_set_message($this->t('The %name configuration was imported.', array('%name' => $this->data['config_name'])));
|
||||
}
|
||||
// For a config entity, create a new entity and save it.
|
||||
// For a config entity, create an entity and save it.
|
||||
else {
|
||||
try {
|
||||
$entity = $this->entityManager
|
||||
->getStorage($this->data['config_type'])
|
||||
->create($this->data['import']);
|
||||
$entity_storage = $this->entityManager->getStorage($this->data['config_type']);
|
||||
if ($this->configExists) {
|
||||
$entity = $entity_storage->updateFromStorageRecord($this->configExists, $this->data['import']);
|
||||
}
|
||||
else {
|
||||
$entity = $entity_storage->createFromStorageRecord($this->data['import']);
|
||||
}
|
||||
$entity->save();
|
||||
drupal_set_message($this->t('The @entity_type %label was imported.', array('@entity_type' => $entity->getEntityTypeId(), '%label' => $entity->label())));
|
||||
}
|
||||
|
|
|
@ -226,6 +226,7 @@ class ConfigCRUDTest extends DrupalUnitTestBase {
|
|||
'boolean' => TRUE,
|
||||
'exp' => 1.2e+34,
|
||||
'float' => 3.14159,
|
||||
'float_as_integer' => (float) 1,
|
||||
'hex' => 0xC,
|
||||
'int' => 99,
|
||||
'octal' => 0775,
|
||||
|
|
|
@ -98,6 +98,26 @@ EOD;
|
|||
$this->assertIdentical($entity->id(), 'second');
|
||||
$this->assertFalse($entity->status());
|
||||
$this->assertIdentical($entity->uuid(), $second_uuid);
|
||||
|
||||
// Perform an update.
|
||||
$import = <<<EOD
|
||||
id: second
|
||||
uuid: $second_uuid
|
||||
label: 'Second updated'
|
||||
weight: 0
|
||||
style: ''
|
||||
status: '0'
|
||||
EOD;
|
||||
$edit = array(
|
||||
'config_type' => 'config_test',
|
||||
'import' => $import,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to update the %name @type?', array('%name' => 'second', '@type' => 'test configuration')));
|
||||
$this->drupalPostForm(NULL, array(), t('Confirm'));
|
||||
$entity = $storage->load('second');
|
||||
$this->assertRaw(t('The @entity_type %label was imported.', array('@entity_type' => 'config_test', '%label' => $entity->label())));
|
||||
$this->assertIdentical($entity->label(), 'Second updated');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,7 @@ array: []
|
|||
boolean: true
|
||||
exp: 1.2e+34
|
||||
float: 3.14159
|
||||
float_as_integer: 1
|
||||
hex: 0xC
|
||||
int: 99
|
||||
octal: 0775
|
||||
|
|
|
@ -68,6 +68,9 @@ config_test.types:
|
|||
float:
|
||||
type: float
|
||||
label: 'Float'
|
||||
float_as_integer:
|
||||
type: float
|
||||
label: 'Float'
|
||||
exp:
|
||||
type: float
|
||||
label: 'Exponential'
|
||||
|
|
|
@ -102,6 +102,16 @@ class FieldImportDeleteUninstallUiTest extends FieldTestBase {
|
|||
$staging->write('core.extension', $core_extension);
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText('This synchronization will delete data from the fields: entity_test.field_tel, entity_test.field_text.');
|
||||
// Delete all the text fields in staging, entity_test_install() adds quite
|
||||
// a few.
|
||||
foreach (\Drupal::entityManager()->getFieldMap() as $entity_type => $fields) {
|
||||
foreach ($fields as $field_name => $info) {
|
||||
if ($info['type'] == 'text') {
|
||||
$staging->delete("field.storage.$entity_type.$field_name");
|
||||
$staging->delete("field.field.$entity_type.$entity_type.$field_name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This will purge all the data, delete the field and uninstall the
|
||||
// Telephone and Text modules.
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\options\Tests\OptionsFloatFieldImportTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\options\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Tests\FieldTestBase;
|
||||
|
||||
/**
|
||||
* Tests option fields can be updated and created through config synchronization.
|
||||
*
|
||||
* @group options
|
||||
*/
|
||||
class OptionsFloatFieldImportTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'options', 'field_ui', 'config', 'options_config_install_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create test user.
|
||||
$admin_user = $this->drupalCreateUser(array('synchronize configuration', 'access content', 'access administration pages', 'administer site configuration', 'administer content types', 'administer nodes', 'bypass node access', 'administer node fields', 'administer node display'));
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that importing list_float fields works.
|
||||
*/
|
||||
public function testImport() {
|
||||
$field_name = 'field_options_float';
|
||||
$type = 'options_install_test';
|
||||
|
||||
// Test the results on installing options_config_install_test. All the
|
||||
// necessary configuration for this test is created by installing that
|
||||
// module.
|
||||
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
|
||||
$this->assertIdentical($field_storage->getSetting('allowed_values'), $array = array('0' => 'Zero', '0.5' => 'Point five'));
|
||||
|
||||
$admin_path = 'admin/structure/types/manage/' . $type . '/fields/node.' . $type . '.' . $field_name . '/storage';
|
||||
|
||||
// Export active config to staging
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
|
||||
|
||||
// Set the active to not use dots in the allowed values key names.
|
||||
$edit = array('field_storage[settings][allowed_values]' => "0|Zero\n1|One");
|
||||
$this->drupalPostForm($admin_path, $edit, t('Save field settings'));
|
||||
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
|
||||
$this->assertIdentical($field_storage->getSetting('allowed_values'), $array = array('0' => 'Zero', '1' => 'One'));
|
||||
|
||||
// Import configuration with dots in the allowed values key names. This
|
||||
// tests \Drupal\Core\Config\Entity\ConfigEntityStorage::importUpdate().
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
|
||||
$this->assertIdentical($field_storage->getSetting('allowed_values'), $array = array('0' => 'Zero', '0.5' => 'Point five'));
|
||||
|
||||
// Delete field to test creation. This tests
|
||||
// \Drupal\Core\Config\Entity\ConfigEntityStorage::importCreate().
|
||||
FieldConfig::loadByName('node', $type, $field_name)->delete();
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$field_storage = FieldStorageConfig::loadByName('node', $field_name);
|
||||
$this->assertIdentical($field_storage->getSetting('allowed_values'), $array = array('0' => 'Zero', '0.5' => 'Point five'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
entity:
|
||||
- field.field.node.options_install_test.body
|
||||
- node.type.options_install_test
|
||||
module:
|
||||
- entity_reference
|
||||
- text
|
||||
id: node.options_install_test.default
|
||||
targetEntityType: node
|
||||
bundle: options_install_test
|
||||
mode: default
|
||||
content:
|
||||
title:
|
||||
type: string_textfield
|
||||
weight: -5
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
uid:
|
||||
type: entity_reference_autocomplete
|
||||
weight: 5
|
||||
settings:
|
||||
match_operator: CONTAINS
|
||||
size: 60
|
||||
autocomplete_type: tags
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
created:
|
||||
type: datetime_timestamp
|
||||
weight: 10
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
promote:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: '1'
|
||||
weight: 15
|
||||
third_party_settings: { }
|
||||
sticky:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: '1'
|
||||
weight: 16
|
||||
third_party_settings: { }
|
||||
body:
|
||||
type: text_textarea_with_summary
|
||||
weight: 26
|
||||
settings:
|
||||
rows: 9
|
||||
summary_rows: 3
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
||||
third_party_settings: { }
|
|
@ -0,0 +1,28 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
entity:
|
||||
- field.field.node.options_install_test.body
|
||||
- node.type.options_install_test
|
||||
module:
|
||||
- text
|
||||
- user
|
||||
id: node.options_install_test.default
|
||||
label: null
|
||||
targetEntityType: node
|
||||
bundle: options_install_test
|
||||
mode: default
|
||||
content:
|
||||
links:
|
||||
weight: 100
|
||||
body:
|
||||
label: hidden
|
||||
type: text_default
|
||||
weight: 101
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
langcode: true
|
||||
third_party_settings:
|
||||
entity_test:
|
||||
foo: bar
|
|
@ -0,0 +1,30 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
entity:
|
||||
- core.entity_view_mode.node.teaser
|
||||
- field.field.node.options_install_test.body
|
||||
- node.type.options_install_test
|
||||
module:
|
||||
- text
|
||||
- user
|
||||
id: node.options_install_test.teaser
|
||||
label: null
|
||||
targetEntityType: node
|
||||
bundle: options_install_test
|
||||
mode: teaser
|
||||
content:
|
||||
links:
|
||||
weight: 100
|
||||
body:
|
||||
label: hidden
|
||||
type: text_summary_or_trimmed
|
||||
weight: 101
|
||||
settings:
|
||||
trim_length: 600
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
langcode: true
|
||||
third_party_settings:
|
||||
entity_test:
|
||||
foo: bar
|
|
@ -0,0 +1,20 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
entity:
|
||||
- field.storage.node.body
|
||||
- node.type.options_install_test
|
||||
id: node.options_install_test.body
|
||||
field_name: body
|
||||
entity_type: node
|
||||
bundle: options_install_test
|
||||
label: Body
|
||||
description: ''
|
||||
required: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
display_summary: true
|
||||
third_party_settings: { }
|
||||
field_type: text_with_summary
|
|
@ -0,0 +1,19 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
entity:
|
||||
- field.storage.node.field_options_float
|
||||
- node.type.options_install_test
|
||||
id: node.options_install_test.field_options_float
|
||||
field_name: field_options_float
|
||||
entity_type: node
|
||||
bundle: options_install_test
|
||||
label: field_options_float
|
||||
description: ''
|
||||
required: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
field_type: list_float
|
|
@ -0,0 +1,24 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- options
|
||||
id: node.field_options_float
|
||||
field_name: field_options_float
|
||||
entity_type: node
|
||||
type: list_float
|
||||
settings:
|
||||
allowed_values:
|
||||
-
|
||||
value: 0
|
||||
label: Zero
|
||||
-
|
||||
value: 0.5
|
||||
label: 'Point five'
|
||||
allowed_values_function: ''
|
||||
module: options
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
|
@ -0,0 +1,11 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
name: options_install_test
|
||||
type: options_install_test
|
||||
description: null
|
||||
help: null
|
||||
new_revision: false
|
||||
preview_mode: 1
|
||||
display_submitted: true
|
||||
third_party_settings: { }
|
|
@ -0,0 +1,9 @@
|
|||
name: 'Options config install test'
|
||||
type: module
|
||||
description: 'Support module for the Options module tests.'
|
||||
core: 8.x
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- node
|
||||
- options
|
Loading…
Reference in New Issue