diff --git a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php deleted file mode 100644 index f801c28..0000000 --- a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php +++ /dev/null @@ -1,117 +0,0 @@ -elements)) { - $this->elements = $this->parse(); - } - return $this->elements; - } - - /** - * Gets valid configuration data keys. - * - * @return array - * Array of valid configuration data keys. - */ - protected function getAllKeys() { - return is_array($this->value) ? array_keys($this->value) : array(); - } - - /** - * Builds an array of contained elements. - * - * @return array - * Array of \Drupal\Core\Config\Schema\ArrayElement objects. - */ - protected abstract function parse(); - - /** - * Implements TypedDataInterface::validate(). - */ - public function validate() { - foreach ($this->getElements() as $element) { - if (!$element->validate()) { - return FALSE; - } - } - return TRUE; - } - - /** - * Implements ArrayAccess::offsetExists(). - */ - public function offsetExists($offset) { - return array_key_exists($offset, $this->getElements()); - } - - /** - * Implements ArrayAccess::offsetGet(). - */ - public function offsetGet($offset) { - $elements = $this->getElements(); - return $elements[$offset]; - } - - /** - * Implements ArrayAccess::offsetSet(). - */ - public function offsetSet($offset, $value) { - if ($value instanceof TypedDataInterface) { - $value = $value->getValue(); - } - $this->value[$offset] = $value; - unset($this->elements); - } - - /** - * Implements ArrayAccess::offsetUnset(). - */ - public function offsetUnset($offset) { - unset($this->value[$offset]); - unset($this->elements); - } - - /** - * Implements Countable::count(). - */ - public function count() { - return count($this->getElements()); - } - - /** - * Implements IteratorAggregate::getIterator(); - */ - public function getIterator() { - return new ArrayIterator($this->getElements()); - } - -} diff --git a/core/lib/Drupal/Core/Config/Schema/Element.php b/core/lib/Drupal/Core/Config/Schema/Element.php index c492d42..f8bd27f 100644 --- a/core/lib/Drupal/Core/Config/Schema/Element.php +++ b/core/lib/Drupal/Core/Config/Schema/Element.php @@ -22,14 +22,33 @@ protected $value; /** - * Create typed config object. + * Creates a contained typed configuration object. + * + * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition + * The data definition object. + * @param mixed $value + * (optional) The data value. If set, it has to match one of the supported + * data type format as documented for the data type classes. + * @param string $name + * The key of the contained element. + * + * @return \Drupal\Core\TypedData\TypedDataInterface */ - protected function parseElement($key, $data, $definition) { - return \Drupal::service('config.typed')->create($definition, $data, $key, $this); + protected function createElement($definition, $value, $key) { + return \Drupal::service('config.typed')->create($definition, $value, $key, $this); } /** - * Build data definition object for contained elements. + * Creates a new data definition object from a type definition array and + * actual configuration data. + * + * @param array $definition + * The base type definition array, for which a data definition should be + * created. + * @param $value + * The value of the configuration element. + * @param string $key + * The key of the contained element. * * @return \Drupal\Core\TypedData\DataDefinitionInterface */ diff --git a/core/lib/Drupal/Core/Config/Schema/Ignore.php b/core/lib/Drupal/Core/Config/Schema/Ignore.php index 20701b8..d005302 100644 --- a/core/lib/Drupal/Core/Config/Schema/Ignore.php +++ b/core/lib/Drupal/Core/Config/Schema/Ignore.php @@ -12,10 +12,4 @@ */ class Ignore extends Element { - /** - * {@inheritdoc}. - */ - public function validate() { - return TRUE; - } } diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php index a85fac4..99dc930 100644 --- a/core/lib/Drupal/Core/Config/Schema/Mapping.php +++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php @@ -13,145 +13,25 @@ /** * Defines a mapping configuration element. * - * Wraps configuration data and metadata allowing access to configuration data - * using the ComplexDataInterface API. This object may contain any number and - * type of nested properties. + * This object may contain any number and type of nested properties and each + * property key may have its own definition in the 'mapping' property of the + * configuration schema. + * + * Properties in the configuration value that are not defined in the mapping + * will get the 'undefined' data type. + * + * Read https://drupal.org/node/1905070 for more details about configuration + * schema, types and type resolution. */ -class Mapping extends ArrayElement implements ComplexDataInterface { - - /** - * An array of data definitions. - * - * @var \Drupal\Core\TypedData\DataDefinitionInterface[] - */ - protected $propertyDefinitions; - - /** - * {@inheritdoc} - */ - protected function parse() { - $elements = array(); - foreach ($this->getPropertyDefinitions() as $key => $definition) { - $elements[$key] = $this->parseElement($key, $this->value[$key], $definition); - } - return $elements; - } +class Mapping extends TypedConfigListBase { /** * {@inheritdoc} - * - * Since all configuration objects are mappings the function will except a dot - * delimited key to access nested values, for example, 'page.front'. - */ - public function get($property_name) { - $parts = explode('.', $property_name); - $root_key = array_shift($parts); - $elements = $this->getElements(); - if (isset($elements[$root_key])) { - $element = $elements[$root_key]; - // If $property_name contained a dot recurse into the keys. - while ($element && ($key = array_shift($parts)) !== NULL) { - if (method_exists($element, 'get')) { - $element = $element->get($key); - } - else { - $element = NULL; - } - } - } - if (isset($element)) { - return $element; - } - else { - throw new \InvalidArgumentException(String::format("The configuration property @key doesn't exist.", array('@key' => $property_name))); - } - } - - /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::set(). - */ - public function set($property_name, $value, $notify = TRUE) { - // Set the data into the configuration array but behave according to the - // interface specification when we've got a null value. - if (isset($value)) { - $this->value[$property_name] = $value; - $property = $this->get($property_name); - } - else { - // In these objects, when clearing the value, the property is gone. - // As this needs to return a property, we get it before we delete it. - $property = $this->get($property_name); - unset($this->value[$property_name]); - $property->setValue($value); - } - // Notify the parent of any changes. - if ($notify && isset($this->parent)) { - $this->parent->onChange($this->name); - } - return $property; - } - - /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::getProperties(). - */ - public function getProperties($include_computed = FALSE) { - return $this->getElements(); - } - - /** - * {@inheritdoc} - */ - public function toArray() { - return $this->getValue(); - } - - /** - * Gets the definition of a contained property. - * - * @param string $name - * The name of property. - * - * @return \Drupal\Core\TypedData\DataDefinitionInterface|null - * The definition of the property or NULL if the property does not exist. - */ - public function getPropertyDefinition($name) { - $definitions = $this->getPropertyDefinitions(); - return isset($definitions[$name]) ? isset($definitions[$name]) : NULL; - } - - /** - * Gets an array of property definitions of contained properties. - * - * @return \Drupal\Core\TypedData\DataDefinitionInterface[] - * An array of property definitions of contained properties, keyed by - * property name. - */ - public function getPropertyDefinitions() { - if (!isset($this->propertyDefinitions)) { - $this->propertyDefinitions = array(); - foreach ($this->getAllKeys() as $key) { - $definition = isset($this->definition['mapping'][$key]) ? $this->definition['mapping'][$key] : array(); - $this->propertyDefinitions[$key] = $this->buildDataDefinition($definition, $this->value[$key], $key); - } - } - return $this->propertyDefinitions; - } - - /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::isEmpty(). - */ - public function isEmpty() { - return empty($this->value); - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::onChange(). */ - public function onChange($property_name) { - // Notify the parent of changes. - if (isset($this->parent)) { - $this->parent->onChange($this->name); - } + public function getElementDefinition($key) { + $value = isset($this->value[$key]) ? $this->value[$key] : NULL; + $definition = isset($this->definition['mapping'][$key]) ? $this->definition['mapping'][$key] : array(); + return $this->buildDataDefinition($definition, $value, $key); } } diff --git a/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php b/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php deleted file mode 100644 index 536a4cb..0000000 --- a/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php +++ /dev/null @@ -1,133 +0,0 @@ -configName = $config_name; - if (!$typed_config->hasConfigSchema($config_name)) { - return FALSE; - } - $definition = $typed_config->getDefinition($config_name); - $data_definition = $typed_config->buildDataDefinition($definition, $config_data); - $this->schema = $typed_config->create($data_definition, $config_data); - $errors = array(); - foreach ($config_data as $key => $value) { - $errors = array_merge($errors, $this->checkValue($key, $value)); - } - if (empty($errors)) { - return TRUE; - } - return $errors; - } - - /** - * Helper method to check data type. - * - * @param string $key - * A string of configuration key. - * @param mixed $value - * Value of given key. - * - * @return array - * List of errors found while checking with the corresponding schema. - */ - protected function checkValue($key, $value) { - $error_key = $this->configName . ':' . $key; - $element = $this->schema->get($key); - if ($element instanceof Undefined) { - // @todo Temporary workaround for https://www.drupal.org/node/2224761. - $key_parts = explode('.', $key); - if (array_pop($key_parts) == 'translation_sync' && strpos($this->configName, 'field.') === 0) { - return array(); - } - return array($error_key => 'Missing schema.'); - } - - // Do not check value if it is defined to be ignored. - if ($element && $element instanceof Ignore) { - return array(); - } - - if ($element && is_scalar($value) || $value === NULL) { - $success = FALSE; - $type = gettype($value); - if ($element instanceof PrimitiveInterface) { - $success = - ($type == 'integer' && $element instanceof IntegerInterface) || - ($type == 'double' && $element instanceof FloatInterface) || - ($type == 'boolean' && $element instanceof BooleanInterface) || - ($type == 'string' && $element instanceof StringInterface) || - // Null values are allowed for all types. - ($value === NULL); - } - $class = get_class($element); - if (!$success) { - return array($error_key => "Variable type is $type but applied schema class is $class."); - } - } - else { - $errors = array(); - if (!$element instanceof ArrayElement) { - $errors[$error_key] = 'Non-scalar value but not defined as an array (such as mapping or sequence).'; - } - - // Go on processing so we can get errors on all levels. Any non-scalar - // value must be an array so cast to an array. - if (!is_array($value)) { - $value = (array) $value; - } - // Recurse into any nested keys. - foreach ($value as $nested_value_key => $nested_value) { - $errors = array_merge($errors, $this->checkValue($key . '.' . $nested_value_key, $nested_value)); - } - return $errors; - } - // No errors found. - return array(); - } -} diff --git a/core/lib/Drupal/Core/Config/Schema/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php index e5b6a35..314e0c5 100644 --- a/core/lib/Drupal/Core/Config/Schema/Sequence.php +++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php @@ -7,86 +7,24 @@ namespace Drupal\Core\Config\Schema; -use Drupal\Core\TypedData\ListInterface; - /** * Defines a configuration element of type Sequence. + * + * This object may contain any number and type of nested elements that share + * a common definition in the 'sequence' property of the configuration schema. + * + * Read https://drupal.org/node/1905070 for more details about configuration + * schema, types and type resolution. */ -class Sequence extends ArrayElement implements ListInterface { - - /** - * Data definition - * - * @var \Drupal\Core\TypedData\DataDefinitionInterface - */ - protected $itemDefinition; - - /** - * Overrides ArrayElement::parse() - */ - protected function parse() { - // Creates a new data definition object for each item from the generic type - // definition array and actual configuration data for that item. Type - // definitions may contain variables to be replaced and those depend on - // each item's data. - $definition = isset($this->definition['sequence'][0]) ? $this->definition['sequence'][0] : array(); - $elements = array(); - foreach ($this->value as $key => $value) { - $data_definition = $this->buildDataDefinition($definition, $value, $key); - $elements[$key] = $this->parseElement($key, $value, $data_definition); - } - return $elements; - } - - /** - * Implements Drupal\Core\TypedData\ListInterface::isEmpty(). - */ - public function isEmpty() { - return empty($this->value); - } - - /** - * Implements Drupal\Core\TypedData\ListInterface::getItemDefinition(). - */ - public function getItemDefinition() { - if (!isset($this->itemDefinition)) { - $definition = isset($this->definition['sequence'][0]) ? $this->definition['sequence'][0] : array(); - $this->itemDefinition = $this->buildDataDefinition($definition, NULL); - } - return $this->itemDefinition; - } - - /** - * Implements \Drupal\Core\TypedData\ListInterface::onChange(). - */ - public function onChange($delta) { - // Notify the parent of changes. - if (isset($this->parent)) { - $this->parent->onChange($this->name); - } - } - - /** - * {@inheritdoc} - */ - public function get($key) { - $elements = $this->getElements(); - return $elements[$key]; - } - - /** - * {@inheritdoc} - */ - public function first() { - return $this->get(0); - } +class Sequence extends TypedConfigListBase { /** * {@inheritdoc} */ - public function set($index, $item) { - $this->offsetSet($index, $item); - return $this; + public function getElementDefinition($key) { + $value = isset($this->value[$key]) ? $this->value[$key] : NULL; + $definition = isset($this->definition['sequence'][0]) ? $this->definition['sequence'][0] : array(); + return $this->buildDataDefinition($definition, $value, $key); } } diff --git a/core/lib/Drupal/Core/Config/Schema/TypedConfig.php b/core/lib/Drupal/Core/Config/Schema/TypedConfig.php new file mode 100644 index 0000000..41e02b5 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/TypedConfig.php @@ -0,0 +1,285 @@ +name = $name; + $this->data = $data; + $this->typedConfigManager = $typed_config; + } + + /** + * Gets raw configuration data. + * + * @return array + * Array of configuration data. + */ + public function getData() { + return $this->data; + } + + /** + * Gets wrapped typed config object. + * + * @return \Drupal\Core\Config\TypedConfigListInterface + * The typed configuration data. + */ + public function getTypedConfigData() { + if (!isset($this->typedConfigData)) { + $this->typedConfigData = $this->typedConfigManager->create($this->getDataDefinition(), $this->data); + } + return $this->typedConfigData; + } + + /** + * Gets the data definition. + * + * @return \Drupal\Core\TypedData\DataDefinitionInterface + * The data definition object. + */ + public function getDataDefinition() { + if (!isset($this->dataDefinition)) { + $type_definition = $this->typedConfigManager->getDefinition($this->name); + return $this->typedConfigManager->buildDataDefinition($type_definition, $this->data); + } + return $this->dataDefinition; + } + + /** + * Gets a contained typed configuration element. + * + * @param $key + * The key of the element to get; e.g., 'title' or 'name'. Nested + * elements can be get using multiple dot delimited names, for example, + * 'page.front'. + * + * @throws \InvalidArgumentException + * If an invalid key is given. + * + * @return \Drupal\Core\TypedData\TypedDataInterface + * The property object. + */ + public function get($key = '') { + $elements = $this->getTypedConfigData(); + if (empty($key)) { + return $elements; + } + $parts = explode('.', $key); + $root_key = array_shift($parts); + + if (isset($elements[$root_key])) { + $element = $elements[$root_key]; + // If $property_name contained a dot recurse into the keys. + while ($element && ($key = array_shift($parts)) !== NULL) { + if ($element instanceof TypedConfigListInterface) { + $element = $element->get($key); + } + else { + $element = NULL; + } + } + } + if (isset($element)) { + return $element; + } + else { + throw new \InvalidArgumentException(String::format("The configuration property @key doesn't exist.", array('@key' => $property_name))); + } + } + + /** + * Replaces the item at the specified position in this list. + * + * @param int|string $key + * Property name or index of the item to replace. + * @param mixed $value + * Value to be stored at the specified position. It can be a raw + * configuration value or \Drupal\Core\TypedData\TypedDataInterface + * + * @return $this + */ + public function set($key, $value) { + // Support setting values via typed data objects. + if ($value instanceof TypedDataInterface) { + $value = $value->getValue(); + } + parent::set($key, $value); + $this->reset(); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setData(array $data) { + parent::setData($data); + $this->reset(); + return $this; + } + + /** + * {@inheritdoc} + */ + public function clear($key) { + parent::clear($key); + $this->reset(); + return $this; + } + + /** + * Reset built typed configuration data and data definition. + */ + protected function reset() { + unset($this->typedConfigData); + unset($this->dataDefinition); + } + + /** + * Checks the TypedConfigManager has a valid schema for the configuration. + * + * @return array|bool + * FALSE if no schema found. List of errors if any found. TRUE if fully + * valid. + */ + public function checkConfigSchema() { + if (!$this->typedConfigManager->hasConfigSchema($this->name)) { + return FALSE; + } + $errors = array(); + foreach ($this->getData() as $key => $value) { + $errors = array_merge($errors, $this->checkValue($key, $value)); + } + if (empty($errors)) { + return TRUE; + } + return $errors; + } + + /** + * Helper method to check data type. + * + * @param string $key + * A string of configuration key. + * @param mixed $value + * Value of given key. + * + * @return array + * List of errors found while checking with the corresponding schema. + */ + protected function checkValue($key, $value) { + $error_key = $this->name . ':' . $key; + $element = $this->get($key); + if ($element instanceof Undefined) { + // @todo Temporary workaround for https://www.drupal.org/node/2224761. + $key_parts = explode('.', $key); + if (array_pop($key_parts) == 'translation_sync' && strpos($this->name, 'field.') === 0) { + return array(); + } + return array($error_key => 'Missing schema.'); + } + + // Do not check value if it is defined to be ignored. + if ($element && $element instanceof Ignore) { + return array(); + } + + if ($element && is_scalar($value) || $value === NULL) { + $success = FALSE; + $type = gettype($value); + if ($element instanceof PrimitiveInterface) { + $success = + ($type == 'integer' && $element instanceof IntegerInterface) || + ($type == 'double' && $element instanceof FloatInterface) || + ($type == 'boolean' && $element instanceof BooleanInterface) || + ($type == 'string' && $element instanceof StringInterface) || + // Null values are allowed for all types. + ($value === NULL); + } + $class = get_class($element); + if (!$success) { + return array($error_key => "Variable type is $type but applied schema class is $class."); + } + } + else { + $errors = array(); + if (!$element instanceof TypedConfigListInterface) { + $errors[$error_key] = 'Non-scalar value but not defined as an array (such as mapping or sequence).'; + } + + // Go on processing so we can get errors on all levels. Any non-scalar + // value must be an array so cast to an array. + if (!is_array($value)) { + $value = (array) $value; + } + // Recurse into any nested keys. + foreach ($value as $nested_value_key => $nested_value) { + $errors = array_merge($errors, $this->checkValue($key . '.' . $nested_value_key, $nested_value)); + } + return $errors; + } + // No errors found. + return array(); + } +} + diff --git a/core/lib/Drupal/Core/Config/Schema/TypedConfigListBase.php b/core/lib/Drupal/Core/Config/Schema/TypedConfigListBase.php new file mode 100644 index 0000000..bb74726 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/TypedConfigListBase.php @@ -0,0 +1,160 @@ +value) ? array_keys($this->value) : array(); + } + + /** + * Builds an array of contained elements. + * + * @return array + * Array of \Drupal\Core\Config\Schema\TypedConfigListBase objects. + */ + protected function parse() { + $elements = array(); + foreach ($this->getAllKeys() as $key) { + $value = isset($this->value[$key]) ? $this->value[$key] : NULL; + $definition = $this->getElementDefinition($key); + $elements[$key] = $this->createElement($definition, $value, $key); + } + return $elements; + } + + /** + * {@inheritdoc} + */ + public function get($property_name) { + $elements = $this->getElements(); + if (isset($elements[$property_name])) { + return $elements[$property_name]; + } + else { + throw new \InvalidArgumentException(String::format("The configuration property @key doesn't exist.", array('@key' => $property_name))); + } + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) { + // Support setting values via typed data objects. + if ($value instanceof TypedDataInterface) { + $value = $value->getValue(); + } + $this->value[$key] = $value; + // Parsed elements must be rebuilt with new values + unset($this->elements); + // Directly notify ourselves. + $this->onChange($key, $value); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getElements() { + if (!isset($this->elements)) { + $this->elements = $this->parse(); + } + return $this->elements; + } + + /** + * {@inheritdoc} + */ + public function isEmpty() { + return empty($this->value); + } + + /** + * {@inheritdoc} + */ + public function toArray() { + return isset($this->value) ? $this->value : array(); + } + + /** + * {@inheritdoc} + */ + public function onChange($key) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->onChange($this->name); + } + } + + /** + * Implements ArrayAccess::offsetExists(). + */ + public function offsetExists($offset) { + return array_key_exists($offset, $this->getElements()); + } + + /** + * Implements ArrayAccess::offsetGet(). + */ + public function offsetGet($offset) { + $elements = $this->getElements(); + return $elements[$offset]; + } + + /** + * Implements ArrayAccess::offsetSet(). + */ + public function offsetSet($offset, $value) { + $this->set($offset, $value); + } + + /** + * Implements ArrayAccess::offsetUnset(). + */ + public function offsetUnset($offset) { + unset($this->value[$offset]); + unset($this->elements); + } + + /** + * Implements Countable::count(). + */ + public function count() { + return count($this->getElements()); + } + + /** + * Implements IteratorAggregate::getIterator(); + */ + public function getIterator() { + return new ArrayIterator($this->getElements()); + } + +} diff --git a/core/lib/Drupal/Core/Config/Schema/TypedConfigListInterface.php b/core/lib/Drupal/Core/Config/Schema/TypedConfigListInterface.php new file mode 100644 index 0000000..0289bfd --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/TypedConfigListInterface.php @@ -0,0 +1,94 @@ +value); - } } diff --git a/core/lib/Drupal/Core/Config/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php index 5c6e8b3..8dfd802 100644 --- a/core/lib/Drupal/Core/Config/StorableConfigBase.php +++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php @@ -9,10 +9,11 @@ use Drupal\Component\Utility\String; use Drupal\Core\Config\Schema\Ignore; +use Drupal\Core\Config\Schema\TypedConfig; +use Drupal\Core\Config\Schema\Undefined; use Drupal\Core\TypedData\PrimitiveInterface; use Drupal\Core\TypedData\Type\FloatInterface; use Drupal\Core\TypedData\Type\IntegerInterface; -use Drupal\Core\Config\Schema\Undefined; /** * Provides a base class for configuration objects with storage support. @@ -122,13 +123,11 @@ public function getStorage() { * data structure, so if the name or the data changes in any way, the wrapper * should be reset. * - * @return \Drupal\Core\Config\Schema\Element + * @return \Drupal\Core\Config\Schema\TypedConfig */ protected function getSchemaWrapper() { if (!isset($this->schemaWrapper)) { - $definition = $this->typedConfigManager->getDefinition($this->name); - $data_definition = $this->typedConfigManager->buildDataDefinition($definition, $this->data); - $this->schemaWrapper = $this->typedConfigManager->create($data_definition, $this->data); + $this->schemaWrapper = new TypedConfig($this->name, $this->data, $this->typedConfigManager); } return $this->schemaWrapper; } diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php index ba3f353..4a24053 100644 --- a/core/lib/Drupal/Core/Config/TypedConfigManager.php +++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Config\Schema\TypedConfig; use Drupal\Core\TypedData\TypedDataManager; /** @@ -73,14 +74,12 @@ public function __construct(StorageInterface $configStorage, StorageInterface $s * @param string $name * Configuration object name. * - * @return \Drupal\Core\Config\Schema\Element - * Typed configuration element. + * @return \Drupal\Core\Config\Schema\TypedConfig + * Typed typed configuration wrapper. */ public function get($name) { $data = $this->configStorage->read($name); - $type_definition = $this->getDefinition($name); - $data_definition = $this->buildDataDefinition($type_definition, $data); - return $this->create($data_definition, $data); + return new TypedConfig($name, $data, $this); } /** diff --git a/core/modules/config/src/Tests/ConfigSchemaTest.php b/core/modules/config/src/Tests/ConfigSchemaTest.php index 7401247..07c74a7 100644 --- a/core/modules/config/src/Tests/ConfigSchemaTest.php +++ b/core/modules/config/src/Tests/ConfigSchemaTest.php @@ -65,21 +65,21 @@ function testSchemaMapping() { // Check type detection on elements with undefined types. $config = \Drupal::service('config.typed')->get('config_schema_test.someschema'); - $definition = $config['testitem']->getDataDefinition()->toArray(); + $definition = $config->get('testitem')->getDataDefinition()->toArray(); $expected = array(); $expected['label'] = 'Test item'; $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; $expected['type'] = 'undefined'; $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; $this->assertEqual($definition, $expected, 'Automatic type detected for a scalar is undefined.'); - $definition = $config['testlist']->getDataDefinition()->toArray(); + $definition = $config->get('testlist')->getDataDefinition()->toArray(); $expected = array(); $expected['label'] = 'Test list'; $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; $expected['type'] = 'undefined'; $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; $this->assertEqual($definition, $expected, 'Automatic type detected for a list is undefined.'); - $definition = $config['testnoschema']->getDataDefinition()->toArray(); + $definition = $config->get('testnoschema')->getDataDefinition()->toArray(); $expected = array(); $expected['label'] = 'Undefined'; $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; @@ -280,8 +280,8 @@ function testSchemaData() { $this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.'); $this->assertEqual($list['front']->getValue(), 'user', 'Got the right value for page.front data from the list.'); - // And test some ComplexDataInterface methods. - $properties = $list->getProperties(); + // And test some TypedConfigListInterface methods. + $properties = $list->getElements(); $this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.'); $values = $list->toArray(); $this->assertTrue(count($values) == 3 && $values['front'] == 'user', 'Got the right property values for site page.'); diff --git a/core/modules/config/src/Tests/SchemaCheckTestTrait.php b/core/modules/config/src/Tests/SchemaCheckTestTrait.php index c5b4cba..a9f31a6 100644 --- a/core/modules/config/src/Tests/SchemaCheckTestTrait.php +++ b/core/modules/config/src/Tests/SchemaCheckTestTrait.php @@ -8,7 +8,7 @@ namespace Drupal\config\Tests; use Drupal\Core\Config\TypedConfigManagerInterface; -use Drupal\Core\Config\Schema\SchemaCheckTrait; +use Drupal\Core\Config\Schema\TypedConfig; use Drupal\Component\Utility\String; /** @@ -16,8 +16,6 @@ */ trait SchemaCheckTestTrait { - use SchemaCheckTrait; - /** * Asserts the TypedConfigManager has a valid schema for the configuration. * @@ -29,7 +27,8 @@ * The configuration data. */ public function assertConfigSchema(TypedConfigManagerInterface $typed_config, $config_name, $config_data) { - $errors = $this->checkConfigSchema($typed_config, $config_name, $config_data); + $config_wrapper = new TypedConfig($config_name, $config_data, $typed_config); + $errors = $config_wrapper->checkConfigSchema(); if ($errors === FALSE) { // @todo Since the use of this trait is under TestBase, it works. // Can be fixed as part of https://drupal.org/node/2260053. diff --git a/core/modules/config/src/Tests/SchemaCheckTraitTest.php b/core/modules/config/src/Tests/SchemaCheckTraitTest.php index f7dd11e..28f4e3c 100644 --- a/core/modules/config/src/Tests/SchemaCheckTraitTest.php +++ b/core/modules/config/src/Tests/SchemaCheckTraitTest.php @@ -7,17 +7,18 @@ namespace Drupal\config\Tests; -use Drupal\Core\Config\Schema\SchemaCheckTrait; +use Drupal\Core\Config\Schema\TypedConfig; use Drupal\simpletest\KernelTestBase; /** * Tests the functionality of SchemaCheckTrait. * + * @todo Rename, that is not a trait anymore + * * @group config */ class SchemaCheckTraitTest extends KernelTestBase { - use SchemaCheckTrait; /** * The typed config manager. @@ -47,19 +48,22 @@ public function setUp() { */ public function testTrait() { // Test a non existing schema. - $ret = $this->checkConfigSchema($this->typedConfig, 'config_schema_test.noschema', \Drupal::config('config_schema_test.noschema')->get()); + $typed_config = new TypedConfig('config_schema_test.noschema', \Drupal::config('config_schema_test.noschema')->get(), $this->typedConfig); + $ret = $typed_config->checkConfigSchema(); $this->assertIdentical($ret, FALSE); // Test an existing schema with valid data. $config_data = \Drupal::config('config_test.types')->get(); - $ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data); + $typed_config = new TypedConfig('config_test.types', $config_data, $this->typedConfig); + $ret = $typed_config->checkConfigSchema(); $this->assertIdentical($ret, TRUE); // Add a new key, a new array and overwrite boolean with array to test the // error messages. $config_data = array('new_key' => 'new_value', 'new_array' => array()) + $config_data; $config_data['boolean'] = array(); - $ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data); + $typed_config = new TypedConfig('config_test.types', $config_data, $this->typedConfig); + $ret = $typed_config->checkConfigSchema(); $expected = array( 'config_test.types:new_key' => 'Missing schema.', 'config_test.types:new_array' => 'Missing schema.', diff --git a/core/modules/config_translation/src/ConfigMapperManager.php b/core/modules/config_translation/src/ConfigMapperManager.php index 8eb3735..40aa5cb 100644 --- a/core/modules/config_translation/src/ConfigMapperManager.php +++ b/core/modules/config_translation/src/ConfigMapperManager.php @@ -10,7 +10,7 @@ use Drupal\Component\Utility\String; use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException; use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Config\Schema\ArrayElement; +use Drupal\Core\Config\Schema\TypedConfigListInterface; use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ThemeHandlerInterface; @@ -161,7 +161,7 @@ protected function findDefinitions() { * {@inheritdoc} */ public function hasTranslatable($name) { - return $this->findTranslatable($this->typedConfigManager->get($name)); + return $this->findTranslatable($this->typedConfigManager->get($name)->get()); } /** @@ -176,7 +176,7 @@ public function hasTranslatable($name) { protected function findTranslatable(TypedDataInterface $element) { // In case this is a sequence or a mapping check whether any child element // is translatable. - if ($element instanceof ArrayElement) { + if ($element instanceof TypedConfigListInterface) { foreach ($element as $child_element) { if ($this->findTranslatable($child_element)) { return TRUE; diff --git a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php index 5b6125a..83707eb 100644 --- a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php +++ b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php @@ -195,7 +195,7 @@ public function buildForm(array $form, array &$form_state, Request $request = NU ); foreach ($this->mapper->getConfigNames() as $name) { $form['config_names'][$name] = array('#type' => 'container'); - $form['config_names'][$name] += $this->buildConfigForm($this->typedConfigManager->get($name), $config_factory->get($name)->get(), $this->baseConfigData[$name]); + $form['config_names'][$name] += $this->buildConfigForm($this->typedConfigManager->get($name)->get(), $config_factory->get($name)->get(), $this->baseConfigData[$name]); } $form['actions']['#type'] = 'actions'; diff --git a/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php b/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php index d7c47cb..528d8aa 100644 --- a/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php +++ b/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php @@ -171,7 +171,7 @@ protected function getElement(array $definition) { */ protected function getNestedElement(array $elements) { // ConfigMapperManager::findTranslatable() checks for the abstract class - // \Drupal\Core\Config\Schema\ArrayElement, but mocking that directly does + // \Drupal\Core\Config\Schema\TypedConfigListBase, but mocking that directly does // not work. $nested_element = $this->getMockBuilder('Drupal\Core\Config\Schema\Mapping') ->disableOriginalConstructor() diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 3dd25b7..3abbeb1 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -629,7 +629,7 @@ function locale_config_update_multiple(array $names, $langcodes = array()) { foreach ($names as $name) { $wrapper = \Drupal\locale\Locale::config()->get($name); foreach ($langcodes as $langcode) { - $translation = $wrapper->getValue() ? $wrapper->getTranslation($langcode)->getValue() : NULL; + $translation = $wrapper->getData() ? $wrapper->getTranslation($langcode)->getValue() : NULL; if ($translation) { \Drupal\locale\Locale::config()->saveTranslationData($name, $langcode, $translation); $count++; diff --git a/core/modules/locale/src/LocaleConfigManager.php b/core/modules/locale/src/LocaleConfigManager.php index 73d653c..687d803 100644 --- a/core/modules/locale/src/LocaleConfigManager.php +++ b/core/modules/locale/src/LocaleConfigManager.php @@ -106,13 +106,9 @@ public function get($name) { $updated = $this->configStorage->read($name); // We get only the data that didn't change from default. $data = $this->compareConfigData($default, $updated); - $definition = $this->typedConfigManager->getDefinition($name); - $data_definition = $this->typedConfigManager->buildDataDefinition($definition, $data); // Unless the configuration has a explicit language code we assume English. $langcode = isset($default['langcode']) ? $default['langcode'] : 'en'; - $wrapper = new LocaleTypedConfig($data_definition, $name, $langcode, $this, $this->typedConfigManager); - $wrapper->setValue($data); - return $wrapper; + return new LocaleTypedConfig($name, $data, $langcode, $this, $this->typedConfigManager); } /** diff --git a/core/modules/locale/src/LocaleTypedConfig.php b/core/modules/locale/src/LocaleTypedConfig.php index cbe9d1a..27a65e8 100644 --- a/core/modules/locale/src/LocaleTypedConfig.php +++ b/core/modules/locale/src/LocaleTypedConfig.php @@ -10,20 +10,14 @@ use Drupal\Core\TypedData\ContextAwareInterface; use Drupal\Core\TypedData\DataDefinitionInterface; use Drupal\Core\Config\Schema\Element; -use Drupal\Core\Config\Schema\ArrayElement; +use Drupal\Core\Config\Schema\TypedConfig; +use Drupal\Core\Config\Schema\TypedConfigListInterface; use Drupal\Core\Config\TypedConfigManagerInterface; /** * Defines the locale configuration wrapper object. */ -class LocaleTypedConfig extends Element { - - /** - * The typed configuration data. - * - * @var \Drupal\Core\Config\Schema\Element - */ - protected $typedConfig; +class LocaleTypedConfig extends TypedConfig { /** * The language code for which this is a translation. @@ -40,19 +34,12 @@ class LocaleTypedConfig extends Element { protected $localeConfig; /** - * The typed config manager. - * - * @var \Drupal\Core\Config\TypedConfigManagerInterface - */ - protected $typedConfigManager; - - /** * Constructs a configuration wrapper object. * - * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition - * The data definition. * @param string $name * The configuration object name. + * @param array $data + * The configuration data. * @param string $langcode * Language code for the source configuration data. * @param \Drupal\locale\LocaleConfigManager $locale_config @@ -60,18 +47,10 @@ class LocaleTypedConfig extends Element { * @param \Drupal\locale\TypedConfigManagerInterface $typed_config; * The typed configuration manager interface. */ - public function __construct(DataDefinitionInterface $definition, $name, $langcode, LocaleConfigManager $locale_config, TypedConfigManagerInterface $typed_config) { - parent::__construct($definition, $name); + public function __construct($name, $data, $langcode, LocaleConfigManager $locale_config, TypedConfigManagerInterface $typed_config) { + parent::__construct($name, $data, $typed_config); $this->langcode = $langcode; $this->localeConfig = $locale_config; - $this->typedConfigManager = $typed_config; - } - - /** - * Gets wrapped typed config object. - */ - public function getTypedConfig() { - return $this->typedConfigManager->create($this->definition, $this->value); } /** @@ -82,8 +61,8 @@ public function getTranslation($langcode) { 'source' => $this->langcode, 'target' => $langcode, ); - $data = $this->getElementTranslation($this->getTypedConfig(), $options); - return $this->typedConfigManager->create($this->definition, $data); + $data = $this->getElementTranslation(clone $this->getTypedConfigData(), $options); + return $this->typedConfigManager->create($this->getDataDefinition(), $data); } /** @@ -116,7 +95,7 @@ protected function canTranslate($from_langcode, $to_langcode) { * * @param mixed $element * Typed configuration element, either \Drupal\Core\Config\Schema\Element or - * \Drupal\Core\Config\Schema\ArrayElement. + * \Drupal\Core\Config\Schema\TypedConfigListInterface. * @param array $options * Array with translation options that must contain the keys defined in * \Drupal\locale\LocaleTypedConfig::translateElement(). @@ -127,7 +106,7 @@ protected function canTranslate($from_langcode, $to_langcode) { */ protected function getElementTranslation($element, array $options) { $translation = array(); - if ($element instanceof ArrayElement) { + if ($element instanceof TypedConfigListInterface) { $translation = $this->getArrayTranslation($element, $options); } elseif ($this->translateElement($element, $options)) { @@ -137,9 +116,9 @@ protected function getElementTranslation($element, array $options) { } /** - * Gets translated configuration data for an element of type ArrayElement. + * Gets translated configuration data for an element of type TypedConfigListInterface. * - * @param \Drupal\Core\Config\Schema\ArrayElement $element + * @param \Drupal\Core\Config\Schema\TypedConfigListInterface $element * Typed configuration array element. * @param array $options * Array with translation options that must contain the keys defined in @@ -148,7 +127,7 @@ protected function getElementTranslation($element, array $options) { * @return array * Configuration data translated to the requested language. */ - protected function getArrayTranslation(ArrayElement $element, array $options) { + protected function getArrayTranslation(TypedConfigListInterface $element, array $options) { $translation = array(); foreach ($element as $key => $property) { $value = $this->getElementTranslation($property, $options); diff --git a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php index 8eaad50..0af5df5 100644 --- a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php @@ -84,7 +84,7 @@ public function testConfigTranslation() { // Get translation and check we've only got the site name. $translation = $wrapper->getTranslation($langcode); - $properties = $translation->getProperties(); + $properties = $translation->getElements(); $this->assertEqual(count($properties), 1, 'Got the right number of properties after translation'); // Check the translated site name is displayed. diff --git a/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php index 0c7b9de..5101a0a 100644 --- a/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php +++ b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php @@ -298,7 +298,7 @@ public function testConfigPoFile() { foreach ($config_strings as $config_key => $config_string) { $wrapper = $locale_config->get($config_key); $translation = $wrapper->getTranslation($langcode); - $properties = $translation->getProperties(); + $properties = $translation->getElements(); $this->assertEqual(count($properties), 1, 'Got the right number of properties with strict translation'); $this->assertEqual($properties[$config_string[2]]->getValue(), $config_string[1]); }