diff --git a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php index 86a21f4..73e9402 100644 --- a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php +++ b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php @@ -7,20 +7,15 @@ namespace Drupal\Core\Config\Schema; +use Drupal\Component\Utility\String; use Drupal\Core\TypedData\TraversableTypedDataInterface; use Drupal\Core\TypedData\TypedData; +use Drupal\Core\TypedData\TypedDataInterface; /** * Defines a generic configuration element that contains multiple properties. */ -abstract class ArrayElement extends TypedData implements \IteratorAggregate, TypedConfigInterface { - - /** - * The configuration value. - * - * @var mixed - */ - protected $value; +abstract class ArrayElement extends TypedData implements \IteratorAggregate, TraversableTypedDataInterface { /** * Parsed elements. @@ -54,16 +49,6 @@ protected function parse() { } /** - * Gets data definition object for contained element. - * - * @param int|string $key - * Property name or index of the element. - * - * @return \Drupal\Core\TypedData\DataDefinitionInterface - */ - protected abstract function getElementDefinition($key); - - /** * {@inheritdoc} */ public function get($name) { @@ -74,7 +59,7 @@ public function get($name) { $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) { + if ($element instanceof TraversableTypedDataInterface) { $element = $element->get($key); } else { @@ -86,14 +71,14 @@ public function get($name) { return $element; } else { - throw new \InvalidArgumentException(String::format("The configuration property @key doesn't exist.", array('@key' => $property_name))); + throw new \InvalidArgumentException(String::format("The configuration property @key doesn't exist.", array('@key' => $name))); } } /** * {@inheritdoc} */ - public function set($key, $value) { + public function set($key, $value, $notify = TRUE) { // Support setting values via typed data objects. if ($value instanceof TypedDataInterface) { $value = $value->getValue(); @@ -109,7 +94,7 @@ public function set($key, $value) { /** * {@inheritdoc} */ - public function getElements() { + public function getElements($include_computed = FALSE) { if (!isset($this->elements)) { $this->elements = $this->parse(); } diff --git a/core/lib/Drupal/Core/Config/Schema/Element.php b/core/lib/Drupal/Core/Config/Schema/Element.php deleted file mode 100644 index 2993f8c..0000000 --- a/core/lib/Drupal/Core/Config/Schema/Element.php +++ /dev/null @@ -1,24 +0,0 @@ -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/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php index b7c34c2..4190330 100644 --- a/core/lib/Drupal/Core/Config/Schema/Sequence.php +++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php @@ -7,6 +7,9 @@ namespace Drupal\Core\Config\Schema; +use Drupal\Core\TypedData\ListInterface; +use Drupal\Core\TypedData\ListTrait; + /** * Defines a configuration element of type Sequence. * @@ -16,15 +19,33 @@ * Read https://drupal.org/node/1905070 for more details about configuration * schema, types and type resolution. */ -class Sequence extends ArrayElement { +class Sequence extends ArrayElement implements ListInterface { + + use ListTrait; + + /** + * {@inheritdoc} + */ + public function getItemDefinition() { + return $this->getElementDefinition(0); + } /** * {@inheritdoc} */ - protected function getElementDefinition($key) { + 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); } + /** + * {@inheritdoc} + */ + public function filter($callback) { + $this->value = array_filter($this->value, $callback); + unset($this->elements); + return $this; + } + } diff --git a/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php b/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php deleted file mode 100644 index 8ce63df..0000000 --- a/core/lib/Drupal/Core/Config/Schema/TypedConfigInterface.php +++ /dev/null @@ -1,78 +0,0 @@ -getFields() as $field_items) { foreach ($field_items as $field_item) { // Loop over all properties of a field item. - foreach ($field_item->getProperties(TRUE) as $property) { + foreach ($field_item->getElements(TRUE) as $property) { if ($property instanceof EntityReference && $entity = $property->getValue()) { $referenced_entities[] = $entity; } diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php index bbcb0bb..8fb5ade 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityAdapter.php @@ -112,7 +112,7 @@ public function set($property_name, $value, $notify = TRUE) { /** * {@inheritdoc} */ - public function getProperties($include_computed = FALSE) { + public function getElements($include_computed = FALSE) { if (!isset($this->entity)) { throw new MissingDataException(String::format('Unable to get properties as no entity has been provided.')); } @@ -127,6 +127,13 @@ public function getProperties($include_computed = FALSE) { /** * {@inheritdoc} */ + public function getElementDefinition($name) { + return $this->getDataDefinition()->getPropertyDefinition($name); + } + + /** + * {@inheritdoc} + */ public function toArray() { if (!isset($this->entity)) { throw new MissingDataException(String::format('Unable to get property values as no entity has been provided.')); @@ -170,7 +177,7 @@ public function getString() { */ public function applyDefaultValue($notify = TRUE) { // Apply the default value of all properties. - foreach ($this->getProperties() as $property) { + foreach ($this->getElements() as $property) { $property->applyDefaultValue(FALSE); } return $this; diff --git a/core/lib/Drupal/Core/Field/FieldItemBase.php b/core/lib/Drupal/Core/Field/FieldItemBase.php index b411b88..f11bd13 100644 --- a/core/lib/Drupal/Core/Field/FieldItemBase.php +++ b/core/lib/Drupal/Core/Field/FieldItemBase.php @@ -51,6 +51,18 @@ public static function mainPropertyName() { */ public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) { parent::__construct($definition, $name, $parent); + // Data types such as Integer or String use a simple value property to + // manage their value, which is why TypedData::getValue() uses that property + // for convencience of those data types. By declaring it as a member + // variable of the TypedData class, however, PHP sets a NULL value to it + // upon instantiation. This breaks field types which have a 'value' property + // as by inheriting from this class they inherit from TypedData as well and, + // thus, $field_item->value returns NULL directly instead of asking + // FieldItemBase::__get() for the proper value stored in + // $field_item->values['value']. Hence, we unset the NULL value. + // @todo Clean this up. + unset($this->value); + // Initialize computed properties by default, such that they get cloned // with the whole item. foreach ($this->definition->getPropertyDefinitions() as $name => $definition) { diff --git a/core/lib/Drupal/Core/Field/FieldItemListInterface.php b/core/lib/Drupal/Core/Field/FieldItemListInterface.php index fcaa9b1..9fde3e8 100644 --- a/core/lib/Drupal/Core/Field/FieldItemListInterface.php +++ b/core/lib/Drupal/Core/Field/FieldItemListInterface.php @@ -28,7 +28,7 @@ * When implementing this interface which extends Traversable, make sure to list * IteratorAggregate or Iterator before this interface in the implements clause. */ -interface FieldItemListInterface extends ListInterface, AccessibleInterface { +interface FieldItemListInterface extends ListInterface, AccessibleInterface, \Countable, \ArrayAccess { /** * Gets the entity that field belongs to. diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php index b60f84d..1a566db 100644 --- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php +++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php @@ -24,79 +24,4 @@ * @ingroup typed_data */ interface ComplexDataInterface extends TraversableTypedDataInterface { - - /** - * Gets a property object. - * - * @param $property_name - * The name of the property to get; e.g., 'title' or 'name'. - * - * @return \Drupal\Core\TypedData\TypedDataInterface - * The property object. - * - * @throws \InvalidArgumentException - * If an invalid property name is given. - * @throws \Drupal\Core\TypedData\Exception\MissingDataException - * If the complex data structure is unset and no property can be created. - */ - public function get($property_name); - - /** - * Sets a property value. - * - * @param $property_name - * The name of the property to set; e.g., 'title' or 'name'. - * @param $value - * The value to set, or NULL to unset the property. - * @param bool $notify - * (optional) Whether to notify the parent object of the change. Defaults to - * TRUE. If the update stems from a parent object, set it to FALSE to avoid - * being notified again. - * - * @return $this - * - * @throws \InvalidArgumentException - * If the specified property does not exist. - * @throws \Drupal\Core\TypedData\Exception\MissingDataException - * If the complex data structure is unset and no property can be set. - */ - public function set($property_name, $value, $notify = TRUE); - - /** - * Gets an array of property objects. - * - * @param bool $include_computed - * If set to TRUE, computed properties are included. Defaults to FALSE. - * - * @return \Drupal\Core\TypedData\TypedDataInterface[] - * An array of property objects implementing the TypedDataInterface, keyed - * by property name. - * - * @throws \Drupal\Core\TypedData\Exception\MissingDataException - * If the complex data structure is unset and no property can be created. - */ - public function getProperties($include_computed = FALSE); - - /** - * Returns an array of all property values. - * - * Gets an array of plain property values including all not-computed - * properties. - * - * @return array - * An array of property values, keyed by property name. - * - * @throws \Drupal\Core\TypedData\Exception\MissingDataException - * If the complex data structure is unset and no property can be created. - */ - public function toArray(); - - /** - * Determines whether the data structure is empty. - * - * @return boolean - * TRUE if the data structure is empty, FALSE otherwise. - */ - public function isEmpty(); - } diff --git a/core/lib/Drupal/Core/TypedData/ListDataDefinition.php b/core/lib/Drupal/Core/TypedData/ListDataDefinition.php index b710a75..f140e53 100644 --- a/core/lib/Drupal/Core/TypedData/ListDataDefinition.php +++ b/core/lib/Drupal/Core/TypedData/ListDataDefinition.php @@ -101,6 +101,13 @@ public function getItemDefinition() { } /** + * {@inheritdoc} + */ + public function getElementDefinition($name) { + return $this->getItemDefinition(); + } + + /** * Sets the item definition. * * @param \Drupal\Core\TypedData\DataDefinition $definition diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php index 09dce07..500467b 100644 --- a/core/lib/Drupal/Core/TypedData/ListInterface.php +++ b/core/lib/Drupal/Core/TypedData/ListInterface.php @@ -20,15 +20,7 @@ * * @ingroup typed_data */ -interface ListInterface extends TraversableTypedDataInterface, \ArrayAccess, \Countable { - - /** - * Determines whether the list contains any non-empty items. - * - * @return boolean - * TRUE if the list is empty, FALSE otherwise. - */ - public function isEmpty(); +interface ListInterface extends TraversableTypedDataInterface { /** * Gets the definition of a contained item. @@ -39,37 +31,6 @@ public function isEmpty(); public function getItemDefinition(); /** - * Returns the item at the specified position in this list. - * - * @param int $index - * Index of the item to return. - * - * @return \Drupal\Core\TypedData\TypedDataInterface - * The item at the specified position in this list. An empty item is created - * if it does not exist yet. - * - * @throws \Drupal\Core\TypedData\Exception\MissingDataException - * If the complex data structure is unset and no item can be created. - */ - public function get($index); - - /** - * Replaces the item at the specified position in this list. - * - * @param int $index - * Index of the item to replace. - * @param mixed - * Item to be stored at the specified position. - * - * @return static - * Returns the list. - * - * @throws \Drupal\Core\TypedData\Exception\MissingDataException - * If the complex data structure is unset and no item can be set. - */ - public function set($index, $item); - - /** * Returns the first item in this list. * * @return \Drupal\Core\TypedData\TypedDataInterface diff --git a/core/lib/Drupal/Core/TypedData/ListTrait.php b/core/lib/Drupal/Core/TypedData/ListTrait.php new file mode 100644 index 0000000..aafed5b --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/ListTrait.php @@ -0,0 +1,31 @@ +get(0); + } + +} + diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php index 52fc0b0..461f388 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php @@ -7,8 +7,9 @@ namespace Drupal\Core\TypedData\Plugin\DataType; -use Drupal\Core\TypedData\ComplexDataInterface; use Drupal\Core\TypedData\ListInterface; +use Drupal\Core\TypedData\ListTrait; +use Drupal\Core\TypedData\TraversableTypedDataInterface; use Drupal\Core\TypedData\TypedData; use Drupal\Core\TypedData\TypedDataInterface; @@ -28,7 +29,9 @@ * definition_class = "\Drupal\Core\TypedData\ListDataDefinition" * ) */ -class ItemList extends TypedData implements \IteratorAggregate, ListInterface { +class ItemList extends TypedData implements \IteratorAggregate, ListInterface, \Countable, \ArrayAccess { + + use ListTrait; /** * Numerically indexed array of items. @@ -115,7 +118,7 @@ public function get($index) { /** * {@inheritdoc} */ - public function set($index, $item) { + public function set($index, $item, $notify = TRUE) { if (is_numeric($index)) { // Support setting values via typed data objects. if ($item instanceof TypedDataInterface) { @@ -132,8 +135,8 @@ public function set($index, $item) { /** * {@inheritdoc} */ - public function first() { - return $this->get(0); + public function getElements($include_computed = FALSE) { + return $this->list; } /** @@ -167,13 +170,20 @@ protected function createItem($offset = 0, $value = NULL) { } /** - * Implements \Drupal\Core\TypedData\ListInterface::getItemDefinition(). + * {@inheritdoc} */ public function getItemDefinition() { return $this->definition->getItemDefinition(); } /** + * {@inheritdoc} + */ + public function getElementDefinition($name) { + return $this->getItemDefinition(); + } + + /** * Implements \ArrayAccess::offsetSet(). */ public function offsetSet($offset, $value) { @@ -199,11 +209,11 @@ public function count() { } /** - * Implements \Drupal\Core\TypedData\ListInterface::isEmpty(). + * {@inheritdoc} */ public function isEmpty() { foreach ($this->list as $item) { - if ($item instanceof ComplexDataInterface || $item instanceof ListInterface) { + if ($item instanceof TraversableTypedDataInterface) { if (!$item->isEmpty()) { return FALSE; } @@ -242,7 +252,7 @@ public function filter($callback) { } /** - * Implements \Drupal\Core\TypedData\ListInterface::onChange(). + * {@inheritdoc} */ public function onChange($delta) { // Notify the parent of changes. @@ -261,4 +271,11 @@ public function __clone() { } } + /** + * {@inheritdoc} + */ + public function toArray() { + return iterator_to_array($this->getIterator()); + } + } diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php index 7694db5..d2d099a 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php @@ -65,6 +65,14 @@ protected function getPropertyDefinitions() { /** * {@inheritdoc} */ + public function getElementDefinition($name) { + return $this->getDataDefinition()->getPropertyDefinition($name); + } + + + /** + * {@inheritdoc} + */ public function getValue() { // Update the values and return them. foreach ($this->properties as $name => $property) { @@ -111,7 +119,7 @@ public function setValue($values, $notify = TRUE) { */ public function getString() { $strings = array(); - foreach ($this->getProperties() as $property) { + foreach ($this->getElements() as $property) { $strings[] = $property->getString(); } // Remove any empty strings resulting from empty items. @@ -168,7 +176,7 @@ protected function writePropertyValue($property_name, $value) { /** * Implements \Drupal\Core\TypedData\ComplexDataInterface::getProperties(). */ - public function getProperties($include_computed = FALSE) { + public function getElements($include_computed = FALSE) { $properties = array(); foreach ($this->definition->getPropertyDefinitions() as $name => $definition) { if ($include_computed || !$definition->isComputed()) { @@ -183,7 +191,7 @@ public function getProperties($include_computed = FALSE) { */ public function toArray() { $values = array(); - foreach ($this->getProperties() as $name => $property) { + foreach ($this->getElements() as $name => $property) { $values[$name] = $property->getValue(); } return $values; @@ -193,7 +201,7 @@ public function toArray() { * Implements \IteratorAggregate::getIterator(). */ public function getIterator() { - return new \ArrayIterator($this->getProperties()); + return new \ArrayIterator($this->getElements()); } /** @@ -246,7 +254,7 @@ public function onChange($property_name, $notify = TRUE) { */ public function applyDefaultValue($notify = TRUE) { // Apply the default value of all properties. - foreach ($this->getProperties() as $property) { + foreach ($this->getElements() as $property) { $property->applyDefaultValue(FALSE); } return $this; diff --git a/core/lib/Drupal/Core/TypedData/TraversableTypedDataInterface.php b/core/lib/Drupal/Core/TypedData/TraversableTypedDataInterface.php index 34b1df5..a37f15a 100644 --- a/core/lib/Drupal/Core/TypedData/TraversableTypedDataInterface.php +++ b/core/lib/Drupal/Core/TypedData/TraversableTypedDataInterface.php @@ -13,6 +13,81 @@ interface TraversableTypedDataInterface extends TypedDataInterface, \Traversable { /** + * Gets an element. + * + * This returns a property object for complex data objects and an item for + * item list objects. + * + * @param string|int $key + * The name of the property or the index of the item to get. + * + * @return \Drupal\Core\TypedData\TypedDataInterface + * The property object. + * + * @throws \InvalidArgumentException + * If an invalid property name or item index is given. + * @throws \Drupal\Core\TypedData\Exception\MissingDataException + * If the data structure is unset and no property can be created. + */ + public function get($key); + + /** + * Sets an element value. + * + * @param $key + * The name of the property or the index of the item to set. + * @param $value + * The value to set, or NULL to unset the property. + * @param bool $notify + * (optional) Whether to notify the parent object of the change. Defaults to + * TRUE. If the update stems from a parent object, set it to FALSE to avoid + * being notified again. + * + * @return \Drupal\Core\TypedData\TypedDataInterface + * The property object. + * + * @throws \InvalidArgumentException + * If the specified property does not exist. + * @throws \Drupal\Core\TypedData\Exception\MissingDataException + * If the complex data structure is unset and no property can be set. + */ + public function set($key, $value, $notify = TRUE); + + /** + * Gets an array of contained elements. + * + * @param bool $include_computed + * (optional) If set to TRUE, computed properties are included. Defaults to + * FALSE. + * + * @return \Drupal\Core\TypedData\TypedDataInterface[] + * An array of the contained objects. + * + * @throws \Drupal\Core\TypedData\Exception\MissingDataException + * If the complex data structure is unset and no property can be created. + */ + public function getElements($include_computed = FALSE); + + /** + * Gets the definition of a contained element. + * + * @param string|int $key + * The name of the property or the index of the item to get. + * + * @return \Drupal\Core\TypedData\DataDefinitionInterface|null + * The definition of the property or NULL if the property does not exist. + */ + public function getElementDefinition($name); + + /** + * Determines whether the data object contains any non-empty items. + * + * @return boolean + * TRUE if the data object is empty, FALSE otherwise. + */ + public function isEmpty(); + + /** * React to changes to a child property or item. * * Note that this is invoked after any changes have been applied. @@ -22,4 +97,18 @@ */ public function onChange($name); + /** + * Returns an array of all property values. + * + * Gets an array of plain property values including all not-computed + * properties. + * + * @return array + * An array of property values, keyed by property name. + * + * @throws \Drupal\Core\TypedData\Exception\MissingDataException + * If the data structure is unset and no property can be created. + */ + public function toArray(); + } diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php index 3216d21..bd7caa7 100644 --- a/core/lib/Drupal/Core/TypedData/TypedData.php +++ b/core/lib/Drupal/Core/TypedData/TypedData.php @@ -44,6 +44,13 @@ protected $parent; /** + * The configuration value. + * + * @var mixed + */ + protected $value; + + /** * {@inheritdoc} */ public static function createInstance($definition, $name = NULL, TraversableTypedDataInterface $parent = NULL) { diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php index d42a3d2..38c1264 100644 --- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php +++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php @@ -74,8 +74,7 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac * - name: (optional) If a property or list item is to be created, the name * of the property or the delta of the list item. * - parent: (optional) If a property or list item is to be created, the - * parent typed data object implementing either the ListInterface or the - * ComplexDataInterface. + * parent typed data object implementing TraversableTypedDataInterface. * * @return \Drupal\Core\TypedData\TypedDataInterface * The instantiated typed data object. @@ -110,10 +109,9 @@ public function createInstance($data_type, array $configuration = array()) { * @param string $name * (optional) If a property or list item is to be created, the name of the * property or the delta of the list item. - * @param mixed $parent + * @param \Drupal\Core\TypedData\TraversableTypedDataInterface $parent * (optional) If a property or list item is to be created, the parent typed - * data object implementing either the ListInterface or the - * ComplexDataInterface. + * data object. * * @return \Drupal\Core\TypedData\TypedDataInterface * The instantiated typed data object. @@ -129,7 +127,7 @@ public function createInstance($data_type, array $configuration = array()) { * @see \Drupal\Core\TypedData\Plugin\DataType\Uri * @see \Drupal\Core\TypedData\Plugin\DataType\Binary */ - public function create(DataDefinitionInterface $definition, $value = NULL, $name = NULL, $parent = NULL) { + public function create(DataDefinitionInterface $definition, $value = NULL, $name = NULL, TraversableTypedDataInterface $parent = NULL) { $typed_data = $this->createInstance($definition->getDataType(), array( 'data_definition' => $definition, 'name' => $name, @@ -196,9 +194,8 @@ public function createListDataDefinition($item_type) { * * @param array $options * An array of options with the following keys: - * - object: The parent typed data object, implementing the - * TypedDataInterface and either the ListInterface or the - * ComplexDataInterface. + * - object: The parent typed data object, implementing + * TraversableTypedDataInterface. * - property: The name of the property to instantiate, or the delta of the * the list item to instantiate. * - value: The value to set. If set, it has to match one of the supported @@ -206,7 +203,7 @@ public function createListDataDefinition($item_type) { * * @throws \InvalidArgumentException * If the given property is not known, or the passed object does not - * implement the ListInterface or the ComplexDataInterface. + * implement TraversableTypedDataInterface. * * @return \Drupal\Core\TypedData\TypedDataInterface * The new property instance. @@ -228,9 +225,8 @@ public function getInstance(array $options) { * property path, i.e. all property instances having the same property path * and inheriting from the same data type are prototyped. * - * @param \Drupal\Core\TypedData\TypedDataInterface $object - * The parent typed data object, implementing the TypedDataInterface and - * either the ListInterface or the ComplexDataInterface. + * @param \Drupal\Core\TypedData\TraversableTypedDataInterface $object + * The parent typed data object. * @param string $property_name * The name of the property to instantiate, or the delta of an list item. * @param mixed $value @@ -239,14 +235,14 @@ public function getInstance(array $options) { * * @throws \InvalidArgumentException * If the given property is not known, or the passed object does not - * implement the ListInterface or the ComplexDataInterface. + * implement TraversableTypedDataInterface. * * @return \Drupal\Core\TypedData\TypedDataInterface * The new property instance. * * @see \Drupal\Core\TypedData\TypedDataManager::create() */ - public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) { + public function getPropertyInstance(TraversableTypedDataInterface $object, $property_name, $value = NULL) { // For performance, try to reuse existing prototypes instead of // constructing new objects when possible. A prototype is reused when // creating a data object: @@ -271,17 +267,16 @@ public function getPropertyInstance(TypedDataInterface $object, $property_name, $parts[] = $object->getPropertyPath() . '.' . (is_numeric($property_name) ? 0 : $property_name); $key = implode(':', $parts); - // Create the prototype if needed. + // Make sure we have a prototype. Then, clone the prototype and set object + // specific values, i.e. the value and the context. if (!isset($this->prototypes[$key])) { - // Fetch the data definition for the child object from the parent. - if ($object instanceof ComplexDataInterface) { - $definition = $object->getDataDefinition()->getPropertyDefinition($property_name); - } - elseif ($object instanceof ListInterface) { - $definition = $object->getItemDefinition(); + // Create the initial prototype. For that we need to fetch the definition + // of the to be created property instance from the parent. + if ($object instanceof TraversableTypedDataInterface) { + $definition = $object->getElementDefinition($property_name); } else { - throw new \InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface."); + throw new \InvalidArgumentException("The passed object has to either implement TraversableTypedDataInterface."); } if (!$definition) { throw new \InvalidArgumentException('Property ' . String::checkPlain($property_name) . ' is unknown.'); diff --git a/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php index 2858daf..53253ff 100644 --- a/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php +++ b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php @@ -7,8 +7,7 @@ namespace Drupal\Core\TypedData\Validation; -use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\Core\TypedData\ListInterface; +use Drupal\Core\TypedData\TraversableTypedDataInterface; use Drupal\Core\TypedData\TypedDataInterface; use Symfony\Component\Validator\MetadataFactoryInterface; @@ -30,7 +29,7 @@ public function getMetadataFor($typed_data, $name = '') { if (!$typed_data instanceof TypedDataInterface) { throw new \InvalidArgumentException('The passed value must be a typed data object.'); } - $is_container = $typed_data instanceof ComplexDataInterface || $typed_data instanceof ListInterface; + $is_container = $typed_data instanceof TraversableTypedDataInterface; $class = '\Drupal\Core\TypedData\Validation\\' . ($is_container ? 'PropertyContainerMetadata' : 'Metadata'); return new $class($typed_data, $name, $this); } diff --git a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php index f5850eb..86c06f8 100644 --- a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php +++ b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php @@ -7,8 +7,7 @@ namespace Drupal\Core\TypedData\Validation; -use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\Core\TypedData\ListInterface; +use Drupal\Core\TypedData\TraversableTypedDataInterface; use Symfony\Component\Validator\PropertyMetadataContainerInterface; use Symfony\Component\Validator\ValidationVisitorInterface; @@ -55,10 +54,7 @@ public function hasPropertyMetadata($property_name) { * Implements PropertyMetadataContainerInterface::getPropertyMetadata(). */ public function getPropertyMetadata($property_name) { - if ($this->typedData instanceof ListInterface) { - return array(new Metadata($this->typedData[$property_name], $property_name)); - } - elseif ($this->typedData instanceof ComplexDataInterface) { + if ($this->typedData instanceof TraversableTypedDataInterface) { return array(new Metadata($this->typedData->get($property_name), $property_name)); } else { diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php index 31ffc5b..0913749 100644 --- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Validation\Plugin\Validation\Constraint; use Drupal\Core\TypedData\ComplexDataInterface; +use Drupal\Core\TypedData\TraversableTypedDataInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; @@ -33,8 +34,7 @@ public function validate($value, Constraint $constraint) { foreach ($constraint->properties as $name => $constraints) { $property = $value->get($name); - $is_container = $property instanceof ComplexDataInterface || $property instanceof ListInterface; - if (!$is_container) { + if (!($property instanceof TraversableTypedDataInterface)) { $property = $property->getValue(); } elseif ($property->isEmpty()) { diff --git a/core/modules/config/src/Tests/ConfigSchemaTest.php b/core/modules/config/src/Tests/ConfigSchemaTest.php index b2c72ea..da3e3b3 100644 --- a/core/modules/config/src/Tests/ConfigSchemaTest.php +++ b/core/modules/config/src/Tests/ConfigSchemaTest.php @@ -281,21 +281,25 @@ function testSchemaData() { $this->assertTrue(empty($definition['translatable']), 'Got the right translatability setting for page.front data.'); // Check nested array of properties. - $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 TypedConfigInterface methods. - $properties = $list; - $this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.'); - $values = $meta->get('page')->toArray(); - $this->assertTrue(count($values) == 3 && $values['front'] == 'user/login', 'Got the right property values for site page.'); + $list = $meta->get('page'); + $this->assertTrue(!empty($list->get('front'))); + $this->assertTrue(!empty($list->get('403'))); + $this->assertTrue(!empty($list->get('404'))); + $this->assertEqual($list->get('front')->getValue(), 'user/login', 'Got the right value for page.front data from the list.'); + + // And test some TraversableTypedDataInterface methods. + $properties = $list->getElements(); + $this->assertIdentical(count($properties), 3); + // assertIdentical() cannot be used here as that uses var_export() on the + // passed objects, but this typed data object contains recursive references. + $this->assertTrue($properties['front'] === $list->get('front')); + $values = $meta->get('page'); + $this->assertIdentical($values->get('front')->getValue(), 'user/login'); // Now let's try something more complex, with nested objects. $wrapper = \Drupal::service('config.typed')->get('image.style.large'); $effects = $wrapper->get('effects'); - $this->assertTrue(count($effects->toArray()) == 1, 'Got an array with effects for image.style.large data'); + $this->assertTrue(count($effects) == 1, 'Got an array with effects for image.style.large data'); $uuid = key($effects->getValue()); $effect = $effects->get($uuid)->getElements(); $this->assertTrue(!$effect['data']->isEmpty() && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.'); diff --git a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php index dbd05fe..6ab80f7 100644 --- a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php +++ b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php @@ -10,7 +10,6 @@ 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; diff --git a/core/modules/locale/src/LocaleTypedConfig.php b/core/modules/locale/src/LocaleTypedConfig.php index ee0b470..3332cbe 100644 --- a/core/modules/locale/src/LocaleTypedConfig.php +++ b/core/modules/locale/src/LocaleTypedConfig.php @@ -9,15 +9,15 @@ use Drupal\Core\TypedData\ContextAwareInterface; use Drupal\Core\TypedData\DataDefinitionInterface; -use Drupal\Core\Config\Schema\Element; use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\TypedData\TraversableTypedDataInterface; +use Drupal\Core\TypedData\TypedData; use Drupal\Core\TypedData\TypedDataInterface; /** * Defines the locale configuration wrapper object. */ -class LocaleTypedConfig extends Element { +class LocaleTypedConfig extends TypedData { /** * The typed configuration data. diff --git a/core/modules/system/src/Tests/Entity/EntityFieldTest.php b/core/modules/system/src/Tests/Entity/EntityFieldTest.php index fdc66e6..4e7a241 100644 --- a/core/modules/system/src/Tests/Entity/EntityFieldTest.php +++ b/core/modules/system/src/Tests/Entity/EntityFieldTest.php @@ -14,6 +14,7 @@ use Drupal\Core\Field\FieldItemInterface; use Drupal\Core\TypedData\ComplexDataDefinitionInterface; use Drupal\Core\TypedData\DataDefinitionInterface; +use Drupal\Core\TypedData\TraversableTypedDataInterface; use Drupal\Core\TypedData\Type\StringInterface; use Drupal\Core\TypedData\TypedDataInterface; use Drupal\node\Entity\NodeType; @@ -564,12 +565,7 @@ public function getContainedStrings(TypedDataInterface $wrapper, $depth, array & // Recurse until a certain depth is reached if possible. if ($depth < 7) { - if ($wrapper instanceof \Drupal\Core\TypedData\ListInterface) { - foreach ($wrapper as $item) { - $this->getContainedStrings($item, $depth + 1, $strings); - } - } - elseif ($wrapper instanceof \Drupal\Core\TypedData\ComplexDataInterface) { + if ($wrapper instanceof TraversableTypedDataInterface) { foreach ($wrapper as $property) { $this->getContainedStrings($property, $depth + 1, $strings); } diff --git a/core/modules/system/src/Tests/TypedData/TypedDataTest.php b/core/modules/system/src/Tests/TypedData/TypedDataTest.php index 99032f4..8ae1d99 100644 --- a/core/modules/system/src/Tests/TypedData/TypedDataTest.php +++ b/core/modules/system/src/Tests/TypedData/TypedDataTest.php @@ -476,14 +476,14 @@ public function testTypedDataMaps() { 'three' => 'drei' )); - $properties = $typed_data->getProperties(); + $properties = $typed_data->getElements(); $this->assertEqual(array_keys($properties), array_keys($value)); $this->assertIdentical($properties['one'], $typed_data->get('one'), 'Properties are identical.'); // Test setting a not defined property. It shouldn't show up in the // properties, but be kept in the values. $typed_data->setValue(array('foo' => 'bar')); - $this->assertEqual(array_keys($typed_data->getProperties()), array('one', 'two', 'three')); + $this->assertEqual(array_keys($typed_data->getElements()), array('one', 'two', 'three')); $this->assertEqual(array_keys($typed_data->getValue()), array('foo', 'one', 'two', 'three')); // Test getting the string representation. diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php index e90d145..3e103b3 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php @@ -973,7 +973,7 @@ public function testCreate() { ->will($this->returnValue(array('id' => 'id'))); // ContentEntityStorageBase iterates over the entity which calls this method - // internally in ContentEntityBase::getProperties(). + // internally in ContentEntityBase::getElements(). $this->entityManager->expects($this->once()) ->method('getFieldDefinitions') ->will($this->returnValue(array())); diff --git a/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php index 0628c89..2d5cbb5 100644 --- a/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/TypedData/EntityAdapterUnitTest.php @@ -322,10 +322,10 @@ public function testSetWithoutData() { } /** - * @covers ::getProperties + * @covers ::getElements */ - public function testGetProperties() { - $fields = $this->entityAdapter->getProperties(); + public function testGetElements() { + $fields = $this->entityAdapter->getElements(); $this->assertInstanceOf('Drupal\Core\Field\FieldItemListInterface', $fields['id']); $this->assertInstanceOf('Drupal\Core\Field\FieldItemListInterface', $fields['revision_id']); }