commit 4cc9eb54d92636bc7249001bf6378e3122c2410d Author: fago Date: Tue Mar 1 22:45:17 2016 +0100 Add config data type. Make sequences complex data. diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml index 4ab0fd7..8ddb55a 100644 --- a/core/config/schema/core.data_types.schema.yml +++ b/core/config/schema/core.data_types.schema.yml @@ -46,7 +46,7 @@ mapping: sequence: label: Sequence class: '\Drupal\Core\Config\Schema\Sequence' - definition_class: '\Drupal\Core\TypedData\ListDataDefinition' + definition_class: '\Drupal\Core\TypedData\MapDataDefinition' # Simple extended data types: diff --git a/core/lib/Drupal/Core/Config/Plugin/DataType/Config.php b/core/lib/Drupal/Core/Config/Plugin/DataType/Config.php new file mode 100644 index 0000000..3377fbf --- /dev/null +++ b/core/lib/Drupal/Core/Config/Plugin/DataType/Config.php @@ -0,0 +1,30 @@ +getTypedDataManager()->create($definition, $value, $key, $this); diff --git a/core/lib/Drupal/Core/Config/Schema/Element.php b/core/lib/Drupal/Core/Config/Schema/Element.php index 86cf4de..8f7d453 100644 --- a/core/lib/Drupal/Core/Config/Schema/Element.php +++ b/core/lib/Drupal/Core/Config/Schema/Element.php @@ -8,13 +8,13 @@ namespace Drupal\Core\Config\Schema; use Drupal\Core\Config\TypedConfigManagerInterface; -use Drupal\Core\TypedData\TypedData; +use Drupal\Core\TypedData\Plugin\DataType\Config; use Drupal\Core\TypedData\TypedDataManagerInterface; /** * Defines a generic configuration element. */ -abstract class Element extends TypedData { +abstract class Element extends Config { /** * The configuration value. diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php index 8646c92..450ee97 100644 --- a/core/lib/Drupal/Core/Config/Schema/Mapping.php +++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php @@ -6,7 +6,8 @@ */ namespace Drupal\Core\Config\Schema; -use Drupal\Core\TypedData\ComplexDataInterface; + +use Drupal\Core\TypedData\MapDataDefinition; /** * Defines a mapping configuration element. @@ -21,7 +22,7 @@ * Read https://www.drupal.org/node/1905070 for more details about configuration * schema, types and type resolution. */ -class Mapping extends ArrayElement implements ComplexDataInterface { +class Mapping extends ArrayElement { /** * {@inheritdoc} diff --git a/core/lib/Drupal/Core/Config/Schema/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php index 9cf5186..048b1b6 100644 --- a/core/lib/Drupal/Core/Config/Schema/Sequence.php +++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php @@ -6,6 +6,7 @@ */ namespace Drupal\Core\Config\Schema; + use Drupal\Core\TypedData\ListInterface; /** @@ -16,6 +17,9 @@ * * Read https://www.drupal.org/node/1905070 for more details about configuration * schema, types and type resolution. + * + * @todo for Drupal 9: Do not implement ListInterface any more as lists are + * just ordered sets without keys, while Sequences have (unknown) keys. */ class Sequence extends ArrayElement implements ListInterface { @@ -35,6 +39,9 @@ protected function getElementDefinition($key) { return $this->buildDataDefinition($definition, $value, $key); } + /** + * @todo for Drupal 9: Do not implement Listinterface and remove this method. + */ public function getItemDefinition() { // @todo. } @@ -42,12 +49,13 @@ public function getItemDefinition() { /** * {@inheritdoc} */ - public function set($index, $value) { + public function set($index, $value, $notify = TRUE) { $this->value[$index] = $value; + return TRUE; } /** - * {@inheritdoc} + * @todo for Drupal 9: Do not implement Listinterface and remove this method. */ public function first() { reset($this->value); @@ -55,21 +63,21 @@ public function first() { } /** - * {@inheritdoc} + * @todo for Drupal 9: Do not implement Listinterface and remove this method. */ public function appendItem($value = NULL) { $this->value[] = $value; } /** - * {@inheritdoc} + * @todo for Drupal 9: Do not implement Listinterface and remove this method. */ public function removeItem($index) { unset($this->value[$index]); } /** - * {@inheritdoc} + * @todo for Drupal 9: Do not implement Listinterface and remove this method. */ public function filter($callback) { return array_filter($this->value, $callback); @@ -110,4 +118,11 @@ public function offsetUnset($offset) { unset($this->elements[$offset]); } + /** + * {@inheritdoc} + */ + public function getProperties($include_computed = FALSE) { + return $this->getElements(); + } + } diff --git a/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php b/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php index f8cda30..850721b 100644 --- a/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php +++ b/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Config\Schema; +use Drupal\Core\TypedData\ComplexDataInterface; use Drupal\Core\TypedData\TraversableTypedDataInterface; /** @@ -19,15 +20,7 @@ * When implementing this interface which extends Traversable, make sure to list * IteratorAggregate or Iterator before this interface in the implements clause. */ -interface TypedConfigInterface extends TraversableTypedDataInterface { - - /** - * Determines whether the data structure is empty. - * - * @return bool - * TRUE if the data structure is empty, FALSE otherwise. - */ - public function isEmpty(); +interface TypedConfigInterface extends TraversableTypedDataInterface, ComplexDataInterface { /** * Gets an array of contained elements. @@ -37,28 +30,4 @@ public function isEmpty(); */ public function getElements(); - /** - * Gets a contained typed configuration element. - * - * @param $name - * The name of the property to get; e.g., 'title' or 'name'. Nested - * elements can be get using multiple dot delimited names, for example, - * 'page.front'. - * - * @return \Drupal\Core\TypedData\TypedDataInterface - * The property object. - * - * @throws \InvalidArgumentException - * If an invalid property name is given. - */ - public function get($name); - - /** - * Returns an array of all property values. - * - * @return array - * An array of property values, keyed by property name. - */ - public function toArray(); - } diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php index 35c8e78..ebd4d2a 100644 --- a/core/lib/Drupal/Core/Config/TypedConfigManager.php +++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php @@ -110,8 +110,9 @@ public function buildDataDefinition(array $definition, $value, $name = NULL, $pa } // Add default values from type definition. $definition += $this->getDefinitionWithReplacements($type, $replace); - - $data_definition = $this->createDataDefinition($definition['type']); + $class = $definition['definition_class']; + $data_definition = $class::create('config'); + $data_definition->setSettings($definition); // Pass remaining values from definition array to data definition. foreach ($definition as $key => $value) { @@ -119,6 +120,10 @@ public function buildDataDefinition(array $definition, $value, $name = NULL, $pa $data_definition[$key] = $value; } } + + // @todo: Add property definitions for all known properties from the mapping + // and sequence keys. + return $data_definition; } @@ -341,7 +346,7 @@ protected function replaceVariable($value, $data) { // Switch replacement values with values from the parent. $parent = $data['%parent']; $data = $parent->getValue(); - $data['%type'] = $parent->getDataDefinition()->getDataType(); + $data['%type'] = $parent->getDataDefinition()->getSetting('type'); // The special %parent and %key values now need to point one level up. if ($new_parent = $parent->getParent()) { $data['%parent'] = $new_parent; diff --git a/core/lib/Drupal/Core/TypedData/DataDefinition.php b/core/lib/Drupal/Core/TypedData/DataDefinition.php index 743fe70..1ef7c49 100644 --- a/core/lib/Drupal/Core/TypedData/DataDefinition.php +++ b/core/lib/Drupal/Core/TypedData/DataDefinition.php @@ -263,7 +263,7 @@ public function setSetting($setting_name, $value) { */ public function getConstraints() { $constraints = isset($this->definition['constraints']) ? $this->definition['constraints'] : array(); - $constraints += $this->getTypedDataManager()->getDefaultConstraints($this); + $constraints += \Drupal::typedDataManager()->getDefaultConstraints($this); return $constraints; } diff --git a/core/tests/Drupal/KernelTests/Config/ConfigValidationTest.php b/core/tests/Drupal/KernelTests/Config/ConfigValidationTest.php deleted file mode 100644 index f75dd8b..0000000 --- a/core/tests/Drupal/KernelTests/Config/ConfigValidationTest.php +++ /dev/null @@ -1,119 +0,0 @@ -installConfig('config_test'); - } - - /** - * Verifies that the Typed Data API is implemented correctly. - */ - public function testTypedDataAPI() { - - /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager */ - $typed_config_manager = \Drupal::service('config.typed'); - /** @var \Drupal\Core\Config\Schema\TypedConfigInterface $typed_config */ - $typed_config = $typed_config_manager->get('config_test.validation'); - - // Test a primitive. - $string_data = $typed_config->get('llama'); - $this->assertInstanceOf(StringInterface::class, $string_data); - $this->assertEquals('meh', $string_data->getValue()); - - // Test complex data. - $mapping = $typed_config->get('cat'); - /** @var \Drupal\Core\TypedData\ComplexDataInterface $mapping */ - $this->assertInstanceOf(ComplexDataInterface::class, $mapping); - $this->assertInstanceOf(StringInterface::class, $mapping->get('type')); - $this->assertEquals('kitten', $mapping->get('type')->getValue()); - $this->assertInstanceOf(IntegerInterface::class, $mapping->get('count')); - $this->assertEquals(2, $mapping->get('count')->getValue()); - // Verify the metadata is available. - $definitions = $mapping->getDataDefinition()->getPropertyDefinitions(); - $this->assertArrayHasKey('type', $definitions); - $this->assertArrayHasKey('count', $definitions); - $this->assertEquals('string', $definitions['type']->getDataType()); - $this->assertEquals('integer', $definitions['count']->getDataType()); - - // Test accessing sequences as list data. Other than sequences, lists do - // not support keys. The items appear just with numeric indices. - $sequence = $typed_config->get('giraffe'); - /** @var \Drupal\Core\TypedData\ListInterface $sequence */ - $this->assertInstanceOf(ListInterface::class, $sequence); - $this->assertInstanceOf(StringInterface::class, $sequence->get(0)); - $this->assertEquals('hum1', $sequence->get(0)->getValue()); - $this->assertEquals('hum2', $sequence->get(1)->getValue()); - $this->assertEquals(2, $sequence->count()); - // Verify the item metadata is available. - $definition = $sequence->getDataDefinition()->getItemDefinition(); - $this->assertEquals('string', $definition->getDataType()); - } - - - public function testSimpleConfigValidation() { - $config = \Drupal::configFactory()->getEditable('config_test.validation'); - /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager */ - $typed_config_manager = \Drupal::service('config.typed'); - /** @var \Drupal\Core\Config\Schema\TypedConfigInterface $typed_config */ - $typed_config = $typed_config_manager->get('config_test.validation'); - - $result = $typed_config->validate(); - $this->assertInstanceOf(ConstraintViolationListInterface::class, $result); - $this->assertEmpty($result); - - // Test constraints on primitive types. - $config->set('llama', 'muh'); - $config->save(); - - $typed_config = $typed_config_manager->get('config_test.validation'); - $result = $typed_config->validate(); - // 1. llama is no longer a string and 2. its not meh anymore. - $this->assertCount(2, $result); - - // Test constraints on mapping - $config->set('cat.type', 'nyans'); - $config->save(); - - $typed_config = $typed_config_manager->get('config_test.validation'); - $result = $typed_config->validate(); - $this->assertEmpty($result); - - $config->set('cat.type', 'miaus'); - $config->save(); - - $typed_config = $typed_config_manager->get('config_test.validation'); - $result = $typed_config->validate(); - $this->assertCount(2, $result); - } - -} diff --git a/core/tests/Drupal/KernelTests/Config/TypedConfigTest.php b/core/tests/Drupal/KernelTests/Config/TypedConfigTest.php new file mode 100644 index 0000000..ff36258 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Config/TypedConfigTest.php @@ -0,0 +1,119 @@ +installConfig('config_test'); + } + + /** + * Verifies that the Typed Data API is implemented correctly. + */ + public function testTypedDataAPI() { + + /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager */ + $typed_config_manager = \Drupal::service('config.typed'); + /** @var \Drupal\Core\Config\Schema\TypedConfigInterface $typed_config */ + $typed_config = $typed_config_manager->get('config_test.validation'); + + // Test a primitive. + $string_data = $typed_config->get('llama'); + $this->assertInstanceOf(StringInterface::class, $string_data); + $this->assertEquals('meh', $string_data->getValue()); + + // Test complex data. + $mapping = $typed_config->get('cat'); + /** @var \Drupal\Core\TypedData\ComplexDataInterface $mapping */ + $this->assertInstanceOf(ComplexDataInterface::class, $mapping); + $this->assertInstanceOf(StringInterface::class, $mapping->get('type')); + $this->assertEquals('kitten', $mapping->get('type')->getValue()); + $this->assertInstanceOf(IntegerInterface::class, $mapping->get('count')); + $this->assertEquals(2, $mapping->get('count')->getValue()); + // Verify the metadata is available. + $definitions = $mapping->getDataDefinition()->getPropertyDefinitions(); + $this->assertArrayHasKey('type', $definitions); + $this->assertArrayHasKey('count', $definitions); + $this->assertEquals('string', $definitions['type']->getDataType()); + $this->assertEquals('integer', $definitions['count']->getDataType()); + + // Test accessing sequences. As they are keyed, they are complex data as + // well. + $sequence = $typed_config->get('giraffe'); + /** @var \Drupal\Core\TypedData\ComplexDataInterface $sequence */ + $this->assertInstanceOf(ComplexDataInterface::class, $sequence); + $this->assertInstanceOf(StringInterface::class, $sequence->get(0)); + $this->assertEquals('hum1', $sequence->get('hum1')->getValue()); + $this->assertEquals('hum2', $sequence->get('hum2')->getValue()); + $this->assertEquals(2, $sequence->count()); + // Verify the item metadata is available. + $definition = $sequence->getDataDefinition()->getItemDefinition(); + $this->assertEquals('string', $definition->getDataType()); + } + + + public function testSimpleConfigValidation() { + $config = \Drupal::configFactory()->getEditable('config_test.validation'); + /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager */ + $typed_config_manager = \Drupal::service('config.typed'); + /** @var \Drupal\Core\Config\Schema\TypedConfigInterface $typed_config */ + $typed_config = $typed_config_manager->get('config_test.validation'); + + $result = $typed_config->validate(); + $this->assertInstanceOf(ConstraintViolationListInterface::class, $result); + $this->assertEmpty($result); + + // Test constraints on primitive types. + $config->set('llama', 'muh'); + $config->save(); + + $typed_config = $typed_config_manager->get('config_test.validation'); + $result = $typed_config->validate(); + // 1. llama is no longer a string and 2. its not meh anymore. + $this->assertCount(2, $result); + + // Test constraints on mapping + $config->set('cat.type', 'nyans'); + $config->save(); + + $typed_config = $typed_config_manager->get('config_test.validation'); + $result = $typed_config->validate(); + $this->assertEmpty($result); + + $config->set('cat.type', 'miaus'); + $config->save(); + + $typed_config = $typed_config_manager->get('config_test.validation'); + $result = $typed_config->validate(); + $this->assertCount(2, $result); + } + +}