Issue #2298687 by tstoeckler, Jose Reyero, hussainweb, rpayanm: Sequence and Mapping implement interfaces incorrectly, make them honest about what they support

8.0.x
Alex Pott 2015-02-19 08:52:47 +00:00
parent fd73d486d7
commit 4337033622
11 changed files with 272 additions and 365 deletions

View File

@ -6,31 +6,35 @@
*/
namespace Drupal\Core\Config\Schema;
use Drupal\Core\TypedData\TraversableTypedDataInterface;
use Drupal\Component\Utility\String;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\TypedData\TypedData;
/**
* Defines a generic configuration element that contains multiple properties.
*/
abstract class ArrayElement extends Element implements \IteratorAggregate, TraversableTypedDataInterface, \ArrayAccess, \Countable {
abstract class ArrayElement extends TypedData implements \IteratorAggregate, TypedConfigInterface {
/**
* The typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfig;
/**
* The configuration value.
*
* @var mixed
*/
protected $value;
/**
* Parsed elements.
*/
protected $elements;
/**
* Gets an array of contained elements.
*
* @return \Drupal\Core\TypedData\TypedDataInterface[]
* An array of elements contained in this element.
*/
protected function getElements() {
if (!isset($this->elements)) {
$this->elements = $this->parse();
}
return $this->elements;
}
/**
* Gets valid configuration data keys.
*
@ -47,59 +51,97 @@ abstract class ArrayElement extends Element implements \IteratorAggregate, Trave
* @return \Drupal\Core\TypedData\TypedDataInterface[]
* An array of elements contained in this element.
*/
protected abstract function parse();
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;
}
/**
* Implements TypedDataInterface::validate().
* Gets data definition object for contained element.
*
* @param int|string $key
* Property name or index of the element.
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface
*/
public function validate() {
foreach ($this->getElements() as $element) {
if (!$element->validate()) {
return FALSE;
protected abstract function getElementDefinition($key);
/**
* {@inheritdoc}
*/
public function get($name) {
$parts = explode('.', $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 ($element instanceof TypedConfigInterface) {
$element = $element->get($key);
}
else {
$element = NULL;
}
}
}
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();
if (isset($element)) {
return $element;
}
else {
throw new \InvalidArgumentException(String::format("The configuration property @key doesn't exist.", array('@key' => $name)));
}
$this->value[$offset] = $value;
unset($this->elements);
}
/**
* Implements ArrayAccess::offsetUnset().
* {@inheritdoc}
*/
public function offsetUnset($offset) {
unset($this->value[$offset]);
public function set($key, $value) {
$this->value[$key] = $value;
// Parsed elements must be rebuilt with new values
unset($this->elements);
// Directly notify ourselves.
$this->onChange($key, $value);
return $this;
}
/**
* Implements Countable::count().
* {@inheritdoc}
*/
public function count() {
return count($this->getElements());
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($name) {
// Notify the parent of changes.
if (isset($this->parent)) {
$this->parent->onChange($this->name);
}
}
/**
@ -109,4 +151,52 @@ abstract class ArrayElement extends Element implements \IteratorAggregate, Trave
return new \ArrayIterator($this->getElements());
}
/**
* 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 $key
* The key of the contained element.
*
* @return \Drupal\Core\TypedData\TypedDataInterface
*/
protected function createElement($definition, $value, $key) {
return $this->typedConfig->create($definition, $value, $key, $this);
}
/**
* 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
*/
protected function buildDataDefinition($definition, $value, $key) {
return $this->typedConfig->buildDataDefinition($definition, $value, $key, $this);
}
/**
* Sets the typed config manager on the instance.
*
* This must be called immediately after construction to enable
* self::parseElement() and self::buildDataDefinition() to work.
*
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
*/
public function setTypedConfig(TypedConfigManagerInterface $typed_config) {
$this->typedConfig = $typed_config;
}
}

View File

@ -7,7 +7,6 @@
namespace Drupal\Core\Config\Schema;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\TypedData\TypedData;
/**
@ -15,13 +14,6 @@ use Drupal\Core\TypedData\TypedData;
*/
abstract class Element extends TypedData {
/**
* The typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfig;
/**
* The configuration value.
*
@ -29,32 +21,4 @@ abstract class Element extends TypedData {
*/
protected $value;
/**
* Create typed config object.
*/
protected function parseElement($key, $data, $definition) {
return $this->typedConfig->create($definition, $data, $key, $this);
}
/**
* Build data definition object for contained elements.
*
* @return \Drupal\Core\TypedData\DataDefinitionInterface
*/
protected function buildDataDefinition($definition, $value, $key) {
return $this->typedConfig->buildDataDefinition($definition, $value, $key, $this);
}
/**
* Sets the typed config manager on the instance.
*
* This must be called immediately after construction to enable
* self::parseElement() and self::buildDataDefinition() to work.
*
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
*/
public function setTypedConfig(TypedConfigManagerInterface $typed_config) {
$this->typedConfig = $typed_config;
}
}

View File

@ -12,10 +12,4 @@ namespace Drupal\Core\Config\Schema;
*/
class Ignore extends Element {
/**
* {@inheritdoc}.
*/
public function validate() {
return TRUE;
}
}

View File

@ -7,151 +7,28 @@
namespace Drupal\Core\Config\Schema;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Component\Utility\String;
/**
* 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;
class Mapping extends ArrayElement {
/**
* {@inheritdoc}
*/
protected function parse() {
$elements = array();
foreach ($this->getPropertyDefinitions() as $key => $definition) {
$elements[$key] = $this->parseElement($key, $this->value[$key], $definition);
}
return $elements;
}
/**
* {@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 $this;
}
/**
* 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);
}
protected 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);
}
}

View File

@ -7,112 +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;
class Sequence extends ArrayElement {
/**
* {@inheritdoc}
*/
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.
protected function getElementDefinition($key) {
$value = isset($this->value[$key]) ? $this->value[$key] : NULL;
$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);
}
/**
* {@inheritdoc}
*/
public function set($index, $value) {
$this->offsetSet($index, $value);
return $this;
}
/**
* {@inheritdoc}
*/
public function removeItem($index) {
$this->offsetUnset($index);
return $this;
}
/**
* {@inheritdoc}
*/
public function appendItem($value = NULL) {
$offset = $this->count();
$this->offsetSet($offset, $value);
return $this->offsetGet($offset);
}
/**
* {@inheritdoc}
*/
public function filter($callback) {
$this->value = array_filter($this->value, $callback);
unset($this->elements);
return $this;
return $this->buildDataDefinition($definition, $value, $key);
}
}

View File

@ -0,0 +1,77 @@
<?php
/**
* @file
* Contains \Drupal\Core\Config\Schema\TypedConfigInterface.
*/
namespace Drupal\Core\Config\Schema;
use Drupal\Core\TypedData\TraversableTypedDataInterface;
/**
* Interface for a typed configuration object that contains multiple elements.
*
* A list of typed configuration contains any number of items whose type
* will depend on the configuration schema but also on the configuration
* data being parsed.
*
* 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 boolean
* TRUE if the data structure is empty, FALSE otherwise.
*/
public function isEmpty();
/**
* Gets an array of contained elements.
*
* @return array
* Array of \Drupal\Core\TypedData\TypedDataInterface objects.
*/
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'.
*
* @throws \InvalidArgumentException
* If an invalid property name is given.
*
* @return \Drupal\Core\TypedData\TypedDataInterface
* The property object.
*/
public function get($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);
/**
* Returns an array of all property values.
*
* @return array
* An array of property values, keyed by property name.
*/
public function toArray();
}

View File

@ -12,10 +12,4 @@ namespace Drupal\Core\Config\Schema;
*/
class Undefined extends Element {
/**
* {@inheritdoc}.
*/
public function validate() {
return isset($this->value);
}
}

View File

@ -10,9 +10,9 @@ namespace Drupal\Core\Config;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\String;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\Schema\ArrayElement;
use Drupal\Core\Config\Schema\ConfigSchemaAlterException;
use Drupal\Core\Config\Schema\ConfigSchemaDiscovery;
use Drupal\Core\Config\Schema\Element;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\TypedData\TypedDataManager;
@ -67,8 +67,8 @@ class TypedConfigManager extends TypedDataManager implements TypedConfigManagerI
* @param string $name
* Configuration object name.
*
* @return \Drupal\Core\Config\Schema\Element
* Typed configuration element.
* @return \Drupal\Core\Config\Schema\TypedConfigInterface
* Typed configuration data.
*/
public function get($name) {
$data = $this->configStorage->read($name);
@ -335,7 +335,7 @@ class TypedConfigManager extends TypedDataManager implements TypedConfigManagerI
$instance = parent::createInstance($data_type, $configuration);
// Enable elements to construct their own definitions using the typed config
// manager.
if ($instance instanceof Element) {
if ($instance instanceof ArrayElement) {
$instance->setTypedConfig($this);
}
return $instance;

View File

@ -69,21 +69,21 @@ class ConfigSchemaTest extends KernelTestBase {
// 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';
@ -197,7 +197,7 @@ class ConfigSchemaTest extends KernelTestBase {
// Most complex case, get metadata for actual configuration element.
$effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects');
$definition = $effects['bddf0d06-42f9-4c75-a700-a33cafa25ea0']['data']->getDataDefinition()->toArray();
$definition = $effects->get('bddf0d06-42f9-4c75-a700-a33cafa25ea0')->get('data')->getDataDefinition()->toArray();
// This should be the schema for image.effect.image_scale, reuse previous one.
$expected['type'] = 'image.effect.image_scale';
@ -244,7 +244,7 @@ class ConfigSchemaTest extends KernelTestBase {
// Test fetching parent one level up.
$entry = $config_data->get('one_level');
$definition = $entry['testitem']->getDataDefinition()->toArray();
$definition = $entry->get('testitem')->getDataDefinition()->toArray();
$expected = array(
'type' => 'config_schema_test.someschema.with_parents.key_1',
'label' => 'Test item nested one level',
@ -255,7 +255,7 @@ class ConfigSchemaTest extends KernelTestBase {
// Test fetching parent two levels up.
$entry = $config_data->get('two_levels');
$definition = $entry['wrapper']['testitem']->getDataDefinition()->toArray();
$definition = $entry->get('wrapper')->get('testitem')->getDataDefinition()->toArray();
$expected = array(
'type' => 'config_schema_test.someschema.with_parents.key_2',
'label' => 'Test item nested two levels',
@ -266,7 +266,7 @@ class ConfigSchemaTest extends KernelTestBase {
// Test fetching parent three levels up.
$entry = $config_data->get('three_levels');
$definition = $entry['wrapper_1']['wrapper_2']['testitem']->getDataDefinition()->toArray();
$definition = $entry->get('wrapper_1')->get('wrapper_2')->get('testitem')->getDataDefinition()->toArray();
$expected = array(
'type' => 'config_schema_test.someschema.with_parents.key_3',
'label' => 'Test item nested three levels',
@ -295,28 +295,26 @@ class ConfigSchemaTest extends KernelTestBase {
$this->assertTrue(empty($definition['translatable']), 'Got the right translatability setting for page.front data.');
// Check nested array of properties.
$list = $meta->get('page');
$list = $meta->get('page')->getElements();
$this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data');
$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/login', 'Got the right value for page.front data from the list.');
// And test some ComplexDataInterface methods.
$properties = $list->getProperties();
// And test some TypedConfigInterface methods.
$properties = $list;
$this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.');
$values = $list->toArray();
$values = $meta->get('page')->toArray();
$this->assertTrue(count($values) == 3 && $values['front'] == 'user/login', 'Got the right property values for site page.');
// Now let's try something more complex, with nested objects.
$wrapper = \Drupal::service('config.typed')->get('image.style.large');
$effects = $wrapper->get('effects');
// The function is_array() doesn't work with ArrayAccess, so we use count().
$this->assertTrue(count($effects) == 1, 'Got an array with effects for image.style.large data');
$this->assertTrue(count($effects->toArray()) == 1, 'Got an array with effects for image.style.large data');
$uuid = key($effects->getValue());
$effect = $effects[$uuid];
$this->assertTrue(count($effect['data']) && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
$this->assertTrue($effect['data']['width'] instanceof IntegerInterface, 'Got the right type for the scale effect width.');
$this->assertEqual($effect['data']['width']->getValue(), 480, 'Got the right value for the scale effect width.' );
$effect = $effects->get($uuid)->getElements();
$this->assertTrue(!$effect['data']->isEmpty() && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
$this->assertTrue($effect['data']->get('width') instanceof IntegerInterface, 'Got the right type for the scale effect width.');
$this->assertEqual($effect['data']->get('width')->getValue(), 480, 'Got the right value for the scale effect width.' );
// Finally update some object using a configuration wrapper.
$new_slogan = 'Site slogan for testing configuration metadata';
@ -423,7 +421,7 @@ class ConfigSchemaTest extends KernelTestBase {
* @see \Drupal\Core\Config\TypedConfigManager::getFallbackName()
*/
function testColonsInSchemaTypeDetermination() {
$tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('tests');
$tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('tests')->getElements();
$definition = $tests[0]->getDataDefinition()->toArray();
$this->assertEqual($definition['type'], 'test.plugin_types.boolean');
@ -436,17 +434,17 @@ class ConfigSchemaTest extends KernelTestBase {
$definition = $tests[3]->getDataDefinition()->toArray();
$this->assertEqual($definition['type'], 'test.plugin_types.*');
$tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('test_with_parents');
$definition = $tests[0]['settings']->getDataDefinition()->toArray();
$tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('test_with_parents')->getElements();
$definition = $tests[0]->get('settings')->getDataDefinition()->toArray();
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean');
$definition = $tests[1]['settings']->getDataDefinition()->toArray();
$definition = $tests[1]->get('settings')->getDataDefinition()->toArray();
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean:*');
$definition = $tests[2]['settings']->getDataDefinition()->toArray();
$definition = $tests[2]->get('settings')->getDataDefinition()->toArray();
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
$definition = $tests[3]['settings']->getDataDefinition()->toArray();
$definition = $tests[3]->get('settings')->getDataDefinition()->toArray();
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
}

View File

@ -10,6 +10,7 @@ namespace Drupal\config_translation\Form;
use Drupal\config_translation\ConfigMapperManagerInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\TypedData\TraversableTypedDataInterface;
use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

View File

@ -83,7 +83,7 @@ class LocaleConfigTranslationTest extends WebTestBase {
// 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.