diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php index ca0eb50..b2c94ed 100644 --- a/core/lib/Drupal/Core/Config/TypedConfigManager.php +++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php @@ -86,6 +86,7 @@ public function buildDataDefinition(array $definition, $value, $name = NULL, $pa // Add default values for data type and replace variables. $definition += array('type' => 'undefined'); + $replace = []; $type = $definition['type']; if (strpos($type, ']')) { // Replace variable names in definition. @@ -102,7 +103,7 @@ public function buildDataDefinition(array $definition, $value, $name = NULL, $pa unset($definition['type']); } // Add default values from type definition. - $definition += $this->getDefinition($type); + $definition += $this->_getDefinitionWithReplacements($type, $replace); $data_definition = $this->createDataDefinition($definition['type']); @@ -116,10 +117,17 @@ public function buildDataDefinition(array $definition, $value, $name = NULL, $pa } /** - * {@inheritdoc} + * Determines the typed config type for a plugin ID. + * + * @param string $base_plugin_id + * The plugin ID. + * @param array $definitions + * An array of typed config definitions. + * + * @return string + * The typed config type for the given plugin ID. */ - public function getDefinition($base_plugin_id, $exception_on_invalid = TRUE) { - $definitions = $this->getDefinitions(); + protected function _determineType($base_plugin_id, array $definitions) { if (isset($definitions[$base_plugin_id])) { $type = $base_plugin_id; } @@ -131,6 +139,27 @@ public function getDefinition($base_plugin_id, $exception_on_invalid = TRUE) { // If we don't have definition, return the 'undefined' element. $type = 'undefined'; } + return $type; + } + + /** + * Gets a schema definition with replacements for dynamic names. + * + * @param string $base_plugin_id + * A plugin ID. + * @param array $replacements + * An array of replacements for dynamic type names. + * @param bool $exception_on_invalid + * (optional) This parameter is passed along to self::getDefinition(). + * However, self::getDefinition() does not respect this parameter, so it is + * effectively useless in this context. + * + * @return array + * A schema definition array. + */ + protected function _getDefinitionWithReplacements($base_plugin_id, array $replacements, $exception_on_invalid = TRUE) { + $definitions = $this->getDefinitions(); + $type = $this->_determineType($base_plugin_id, $definitions); $definition = $definitions[$type]; // Check whether this type is an extension of another one and compile it. if (isset($definition['type'])) { @@ -138,6 +167,15 @@ public function getDefinition($base_plugin_id, $exception_on_invalid = TRUE) { // Preserve integer keys on merge, so sequence item types can override // parent settings as opposed to adding unused second, third, etc. items. $definition = NestedArray::mergeDeepArray(array($merge, $definition), TRUE); + + // Replace dynamic portions of the definition type. + if (!empty($replacements) && strpos($definition['type'], ']')) { + $sub_type = $this->_determineType($this->replaceName($definition['type'], $replacements), $definitions); + // Merge the newly determined subtype definition with the original + // definition. + $definition = NestedArray::mergeDeepArray([$definitions[$sub_type], $definition], TRUE); + } + // Unset type so we try the merge only once per type. unset($definition['type']); $this->definitions[$type] = $definition; @@ -153,6 +191,13 @@ public function getDefinition($base_plugin_id, $exception_on_invalid = TRUE) { /** * {@inheritdoc} */ + public function getDefinition($base_plugin_id, $exception_on_invalid = TRUE) { + return $this->_getDefinitionWithReplacements($base_plugin_id, [], $exception_on_invalid); + } + + /** + * {@inheritdoc} + */ public function clearCachedDefinitions() { $this->schemaStorage->reset(); parent::clearCachedDefinitions(); diff --git a/core/modules/config/src/Tests/ConfigSchemaTest.php b/core/modules/config/src/Tests/ConfigSchemaTest.php index 8735db1..a34e6a6 100644 --- a/core/modules/config/src/Tests/ConfigSchemaTest.php +++ b/core/modules/config/src/Tests/ConfigSchemaTest.php @@ -506,4 +506,70 @@ public function testConfigSchemaInfoAlter() { $this->assertEqual($definitions['config_schema_test.hook']['additional_metadata'], 'new schema info'); } + /** + * Tests saving config when the type is wrapped by a dynamic type. + */ + public function testConfigSaveWithWrappingSchema() { + $untyped_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'plugin_id' => 'wrapper:foo', + 'internal_value' => 100, + ], + ], + ]; + + $typed_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'plugin_id' => 'wrapper:foo', + 'internal_value' => '100', + ], + ], + ]; + + // Save config which has a schema that enforces types. + \Drupal::configFactory()->getEditable('wrapping.config_schema_test.plugin_types') + ->setData($untyped_values) + ->save(); + $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.plugin_types') + ->get(), $typed_values); + } + + /** + * Tests dynamic config schema type with multiple sub-key references. + */ + public function testConfigSaveWithWrappingSchemaDoubleBrackets() { + $untyped_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'foo' => 'cat', + 'bar' => 'dog', + 'another_key' => 100, + ], + ], + ]; + + $typed_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'foo' => 'cat', + 'bar' => 'dog', + 'another_key' => '100', + ], + ], + ]; + + // Save config which has a schema that enforces types. + \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets') + ->setData($untyped_values) + ->save(); + $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets') + ->get(), $typed_values); + } + } diff --git a/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml b/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml index 004ae2b..d7ec9a5 100644 --- a/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml +++ b/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml @@ -213,3 +213,50 @@ config_test.dynamic.*.third_party.config_schema_test: type: integer string: type: string + +wrapping.config_schema_test.plugin_types: + type: config_object + mapping: + tests: + type: sequence + sequence: + - type: wrapping.test.plugin_types.[plugin_id] + +wrapping.test.plugin_types.*: + type: test.plugin_types.[plugin_id] + mapping: + wrapper_value: + type: string + +test.plugin_types.wrapper:*: + type: test.plugin_types + mapping: + internal_value: + type: string + +wrapping.config_schema_test.double_brackets: + type: config_object + mapping: + tests: + type: sequence + sequence: + - type: wrapping.test.double_brackets.[another_key] + +wrapping.test.double_brackets.*: + type: test.double_brackets.[foo].[bar] + mapping: + wrapper_value: + type: string + +test.double_brackets.cat.dog: + type: test.double_brackets + mapping: + another_key: + type: string + foo: + type: string + bar: + type: string + +test.double_brackets.*: + type: mapping