diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php index b39ff27..d5421b9 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Config\Entity; use Drupal\Core\Entity\Entity; -use Drupal\Core\TypedData\ContextAwareInterface; +use Drupal\Core\TypedData\TypedDataInterface; /** * Defines a base configuration entity class. @@ -178,37 +178,37 @@ public function getOriginalEntity() { } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getName(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getName(). */ public function getName() { return NULL; } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getRoot(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getRoot(). */ public function getRoot() { return $this; } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getPropertyPath(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getPropertyPath(). */ public function getPropertyPath() { return ''; } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getParent(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getParent(). */ public function getParent() { return NULL; } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::setContext(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::setContext(). */ - public function setContext($name = NULL, ContextAwareInterface $parent = NULL) { + public function setContext($name = NULL, TypedDataInterface $parent = NULL) { // As entities are always the root of the tree, we do not need to set any // context. } diff --git a/core/lib/Drupal/Core/Config/Schema/Element.php b/core/lib/Drupal/Core/Config/Schema/Element.php index efe7147..d4e29db 100644 --- a/core/lib/Drupal/Core/Config/Schema/Element.php +++ b/core/lib/Drupal/Core/Config/Schema/Element.php @@ -7,12 +7,12 @@ namespace Drupal\Core\Config\Schema; -use Drupal\Core\TypedData\ContextAwareTypedData; +use Drupal\Core\TypedData\TypedData; /** * Defines a generic configuration element. */ -abstract class Element extends ContextAwareTypedData { +abstract class Element extends TypedData { /** * The configuration value. diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index fe00ef3..49d2933 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -9,7 +9,7 @@ use Drupal\Component\Uuid\Uuid; use Drupal\Core\Language\Language; -use Drupal\Core\TypedData\ContextAwareInterface; +use Drupal\Core\TypedData\TypedDataInterface; use IteratorAggregate; /** @@ -251,6 +251,7 @@ public function isEmpty() { public function getIterator() { // @todo: Replace by EntityNG implementation once all entity types have been // converted to use the entity field API. + return new \ArrayIterator(array()); } /** @@ -406,37 +407,103 @@ public function getOriginalEntity() { } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getName(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getType(). + */ + public function getType() { + // @todo: Incorporate the entity type here by making entities proper + // typed data. See http://drupal.org/node/1868004. + return 'entity'; + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getDefinition(). + */ + public function getDefinition() { + return array( + 'type' => $this->getType() + ); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getValue(). + */ + public function getValue() { + // @todo: Implement by making entities proper typed data. See + // http://drupal.org/node/1868004. + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue(). + */ + public function setValue($value, $notify = TRUE) { + // @todo: Implement by making entities proper typed data. See + // http://drupal.org/node/1868004. + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getString(). + */ + public function getString() { + // @todo: Implement by making entities proper typed data. See + // http://drupal.org/node/1868004. + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getConstraints(). + */ + public function getConstraints() { + // @todo: Implement by making entities proper typed data. See + // http://drupal.org/node/1868004. + return array(); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::validate(). + */ + public function validate() { + // @todo: Implement by making entities proper typed data. See + // http://drupal.org/node/1868004. + } + + /** + * Implements \Drupal\Core\TypedData\ComplexDataInterface::handleChanges(). + */ + public function handleChanges($property_name, TypedDataInterface $property, $new_value) { + // Nothing to do. + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getName(). */ public function getName() { return NULL; } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getRoot(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getRoot(). */ public function getRoot() { return $this; } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getPropertyPath(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getPropertyPath(). */ public function getPropertyPath() { return ''; } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getParent(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getParent(). */ public function getParent() { return NULL; } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::setContext(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::setContext(). */ - public function setContext($name = NULL, ContextAwareInterface $parent = NULL) { + public function setContext($name = NULL, TypedDataInterface $parent = NULL) { // As entities are always the root of the tree of typed data, we do not need // to set any parent or name. } diff --git a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php index a23f7e4..3b8a069 100644 --- a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php +++ b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php @@ -9,7 +9,7 @@ use IteratorAggregate; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\TypedData\ContextAwareInterface; +use Drupal\Core\TypedData\TypedDataInterface; /** * Provides backwards compatible (BC) access to entity fields. @@ -422,6 +422,55 @@ public function getTranslation($langcode, $strict = TRUE) { /** * Forwards the call to the decorated entity. */ + public function getType() { + return $this->decorated->getType(); + } + + /** + * Forwards the call to the decorated entity. + */ + public function getDefinition() { + return $this->decorated->getDefinition(); + } + + /** + * Forwards the call to the decorated entity. + */ + public function getValue() { + return $this->decorated->getValue(); + } + + /** + * Forwards the call to the decorated entity. + */ + public function setValue($value, $notify = TRUE) { + return $this->decorated->setValue($value); + } + + /** + * Forwards the call to the decorated entity. + */ + public function getString() { + return $this->decorated->getString(); + } + + /** + * Forwards the call to the decorated entity. + */ + public function getConstraints() { + return $this->decorated->getConstraints(); + } + + /** + * Forwards the call to the decorated entity. + */ + public function validate() { + return $this->decorated->validate(); + } + + /** + * Forwards the call to the decorated entity. + */ public function getName() { return $this->decorated->getName(); } @@ -450,7 +499,7 @@ public function getParent() { /** * Forwards the call to the decorated entity. */ - public function setContext($name = NULL, ContextAwareInterface $parent = NULL) { + public function setContext($name = NULL, TypedDataInterface $parent = NULL) { $this->decorated->setContext($name, $parent); } @@ -460,4 +509,11 @@ public function setContext($name = NULL, ContextAwareInterface $parent = NULL) { public function getExportProperties() { $this->decorated->getExportProperties(); } + + /** + * Forwards the call to the decorated entity. + */ + public function handleChanges($property_name, TypedDataInterface $property, $new_value) { + $this->decorated->handleChanges($property_name, $property, $new_value); + } } diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php index 34038e9..2a8444a 100644 --- a/core/lib/Drupal/Core/Entity/EntityInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityInterface.php @@ -8,7 +8,6 @@ namespace Drupal\Core\Entity; use Drupal\Core\TypedData\AccessibleInterface; -use Drupal\Core\TypedData\ContextAwareInterface; use Drupal\Core\TypedData\ComplexDataInterface; use Drupal\Core\TypedData\TranslatableInterface; @@ -28,7 +27,7 @@ * @see \Drupal\Core\TypedData\TypedDataManager * @see \Drupal\Core\Field\FieldInterface */ -interface EntityInterface extends ContextAwareInterface, ComplexDataInterface, AccessibleInterface, TranslatableInterface { +interface EntityInterface extends ComplexDataInterface, AccessibleInterface, TranslatableInterface { /** * Returns the entity identifier (the entity's machine name or numeric ID). diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php index 70ac362..14403bb 100644 --- a/core/lib/Drupal/Core/Entity/EntityNG.php +++ b/core/lib/Drupal/Core/Entity/EntityNG.php @@ -8,7 +8,6 @@ namespace Drupal\Core\Entity; use Drupal\Core\Language\Language; -use Drupal\Core\TypedData\ContextAwareInterface; use Drupal\Core\TypedData\TypedDataInterface; use Drupal\Component\Uuid\Uuid; use ArrayIterator; @@ -170,6 +169,7 @@ protected function getTranslatedField($property_name, $langcode) { if (isset($this->values[$property_name][$langcode])) { $value = $this->values[$property_name][$langcode]; } + // @todo: Make entities implement the TypedDataInterface. $this->fields[$property_name][$langcode] = typed_data()->getPropertyInstance($this, $property_name, $value); } } @@ -318,9 +318,7 @@ public function getTranslation($langcode, $strict = TRUE) { ); $translation = typed_data()->create($translation_definition, $fields); $translation->setStrictMode($strict); - if ($translation instanceof ContextAwareInterface) { - $translation->setContext('@' . $langcode, $this); - } + $translation->setContext('@' . $langcode, $this); return $translation; } @@ -435,7 +433,7 @@ public function &__get($name) { */ public function __set($name, $value) { // Support setting values via property objects. - if ($value instanceof TypedDataInterface) { + if ($value instanceof TypedDataInterface && !$value instanceof EntityInterface) { $value = $value->getValue(); } // If this is an entity field, handle it accordingly. We first check whether @@ -508,9 +506,7 @@ public function __clone() { foreach ($this->fields as $name => $properties) { foreach ($properties as $langcode => $property) { $this->fields[$name][$langcode] = clone $property; - if ($property instanceof ContextAwareInterface) { - $this->fields[$name][$langcode]->setContext($name, $this); - } + $this->fields[$name][$langcode]->setContext($name, $this); } } } diff --git a/core/lib/Drupal/Core/Entity/Field/FieldInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldInterface.php index 81f3828..7b1d235 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldInterface.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldInterface.php @@ -8,9 +8,7 @@ namespace Drupal\Core\Entity\Field; use Drupal\Core\TypedData\AccessibleInterface; -use Drupal\Core\TypedData\ContextAwareInterface; use Drupal\Core\TypedData\ListInterface; -use Drupal\Core\TypedData\TypedDataInterface; /** * Interface for fields, being lists of field items. @@ -27,7 +25,7 @@ * When implementing this interface which extends Traversable, make sure to list * IteratorAggregate or Iterator before this interface in the implements clause. */ -interface FieldInterface extends ListInterface, AccessibleInterface, ContextAwareInterface, TypedDataInterface { +interface FieldInterface extends ListInterface, AccessibleInterface { /** * Gets a property object from the first field item. diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php index 62edc8d..701b461 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php @@ -7,13 +7,9 @@ namespace Drupal\Core\Entity\Field; -use Drupal\Core\TypedData\ContextAwareTypedData; -use Drupal\Core\TypedData\ContextAwareInterface; +use Drupal\Core\TypedData\Type\Map; use Drupal\Core\TypedData\TypedDataInterface; use Drupal\user; -use ArrayIterator; -use IteratorAggregate; -use InvalidArgumentException; /** * An entity field item. @@ -23,62 +19,7 @@ * * @see \Drupal\Core\Entity\Field\FieldItemInterface */ -abstract class FieldItemBase extends ContextAwareTypedData implements IteratorAggregate, FieldItemInterface { - - /** - * The array of properties, each implementing the TypedDataInterface. - * - * Field objects are instantiated during object construction and cannot be - * replaced by others, so computed properties can safely store references on - * other properties. - * - * @var array - */ - protected $properties = array(); - - /** - * Holds any non-property values that get set on the object. - * - * @todo: Remove or refactor once EntityNG conversion is complete. - * - * @var array - */ - protected $extraValues = array(); - - /** - * Overrides ContextAwareTypedData::__construct(). - */ - public function __construct(array $definition, $name = NULL, ContextAwareInterface $parent = NULL) { - parent::__construct($definition, $name, $parent); - - // Initialize all property objects, but postpone the creating of computed - // properties to a second step. That way computed properties can safely get - // references on non-computed properties during construction. - $step2 = array(); - foreach ($this->getPropertyDefinitions() as $name => $definition) { - if (empty($definition['computed'])) { - $this->properties[$name] = typed_data()->getPropertyInstance($this, $name); - } - else { - $step2[] = $name; - } - } - - foreach ($step2 as $name) { - $this->properties[$name] = typed_data()->getPropertyInstance($this, $name); - } - } - - /** - * Overrides \Drupal\Core\TypedData\TypedData::getValue(). - */ - public function getValue() { - $values = array(); - foreach ($this->getProperties() as $name => $property) { - $values[$name] = $property->getValue(); - } - return $values + $this->extraValues; - } +abstract class FieldItemBase extends Map implements FieldItemInterface { /** * Overrides \Drupal\Core\TypedData\TypedData::setValue(). @@ -86,66 +27,27 @@ public function getValue() { * @param array|null $values * An array of property values. */ - public function setValue($values) { + public function setValue($values, $notify = TRUE) { // Treat the values as property value of the first property, if no array is // given. if (!is_array($values)) { $keys = array_keys($this->properties); $values = array($keys[0] => $values); } - - foreach ($this->properties as $name => $property) { - if (isset($values[$name])) { - $property->setValue($values[$name]); - } - else { - $property->setValue(NULL); - } - unset($values[$name]); - } - // @todo: Throw an exception for invalid values once conversion is - // totally completed. - $this->extraValues = $values; - } - - /** - * Overrides \Drupal\Core\TypedData\TypedData::getString(). - */ - public function getString() { - $strings = array(); - foreach ($this->getProperties() as $property) { - $strings[] = $property->getString(); - } - return implode(', ', array_filter($strings)); - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::get(). - */ - public function get($property_name) { - if (!isset($this->properties[$property_name])) { - throw new InvalidArgumentException('Field ' . check_plain($property_name) . ' is unknown.'); - } - return $this->properties[$property_name]; - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::set(). - */ - public function set($property_name, $value) { - $this->get($property_name)->setValue($value); + parent::setValue($values, $notify); } /** * Implements \Drupal\Core\Entity\Field\FieldItemInterface::__get(). */ public function __get($name) { - if (isset($this->properties[$name])) { - return $this->properties[$name]->getValue(); + // Directly return the plain value, if we have any. + if (isset($this->values[$name]) || array_key_exists($name, $this->values)) { + return $this->values[$name]; } - // The property is unknown, so try to get it from the extra values. - elseif (isset($this->extraValues[$name])) { - return $this->extraValues[$name]; + else { + // Go via the property object, so e.g. computed properties are computed. + return $this->get($name)->getValue(); } } @@ -157,101 +59,33 @@ public function __set($name, $value) { if ($value instanceof TypedDataInterface) { $value = $value->getValue(); } - if (isset($this->properties[$name])) { - $this->properties[$name]->setValue($value); - } - else { - // The property is unknown, so set it to the extra values. - $this->extraValues[$name] = $value; - } + $this->set($name, $value); } /** * Implements \Drupal\Core\Entity\Field\FieldItemInterface::__isset(). */ public function __isset($name) { - return isset($this->properties[$name]) && $this->properties[$name]->getValue() !== NULL; + return isset($this->values[$name]) || isset($this->properties[$name]); } /** * Implements \Drupal\Core\Entity\Field\FieldItemInterface::__unset(). */ public function __unset($name) { - if (isset($this->properties[$name])) { - $this->properties[$name]->setValue(NULL); - } - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::getProperties(). - */ - public function getProperties($include_computed = FALSE) { - $properties = array(); - foreach ($this->getPropertyDefinitions() as $name => $definition) { - if ($include_computed || empty($definition['computed'])) { - $properties[$name] = $this->properties[$name]; - } - } - return $properties; - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues(). - */ - public function getPropertyValues() { - return $this->getValue(); - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues(). - */ - public function setPropertyValues($values) { - foreach ($values as $name => $value) { - $this->get($name)->setValue($value); - } - } - - /** - * Implements \IteratorAggregate::getIterator(). - */ - public function getIterator() { - return new ArrayIterator($this->getProperties()); - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition(). - */ - public function getPropertyDefinition($name) { - $definitions = $this->getPropertyDefinitions(); - if (isset($definitions[$name])) { - return $definitions[$name]; - } - else { - return FALSE; - } - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::isEmpty(). - */ - public function isEmpty() { - foreach ($this->getProperties() as $property) { - if ($property->getValue() !== NULL) { - return FALSE; - } - } - return TRUE; + $this->set($name, NULL); } /** - * Magic method: Implements a deep clone. + * Overrides \Drupal\Core\TypedData\Map::handleChanges(). */ - public function __clone() { - foreach ($this->properties as $name => $property) { - $this->properties[$name] = clone $property; - if ($property instanceof ContextAwareInterface) { - $this->properties[$name]->setContext($name, $this); - } + public function handleChanges($property_name, TypedDataInterface $property, $new_value) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->handleChanges($this->name, $property, $new_value); } + // Remove the plain value, such that any further __get() calls go via the + // updated property object. + unset($this->values[$property_name]); } } \ No newline at end of file diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldItemInterface.php index d115be5..31adec9 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldItemInterface.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldItemInterface.php @@ -8,8 +8,6 @@ namespace Drupal\Core\Entity\Field; use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\Core\TypedData\ContextAwareInterface; -use Drupal\Core\TypedData\TypedDataInterface; /** * Interface for entity field items. @@ -23,7 +21,7 @@ * @see \Drupal\Core\Entity\Field\FieldInterface * @see \Drupal\Core\Entity\Field\FieldItemBase */ -interface FieldItemInterface extends ComplexDataInterface, ContextAwareInterface, TypedDataInterface { +interface FieldItemInterface extends ComplexDataInterface { /** * Magic method: Gets a property value. diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php index 695a7ca..330eb99 100644 --- a/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php +++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php @@ -27,6 +27,16 @@ class EntityReferenceItem extends FieldItemBase { static $propertyDefinitions; /** + * Overrides \Drupal\Core\TypedData\TypedData::__construct(). + */ + public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) { + $this->definition = $definition; + $this->setContext($name, $parent); + // Initialize the entity property by default. + $this->properties['entity'] = typed_data()->getPropertyInstance($this, 'entity'); + } + + /** * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions(). */ public function getPropertyDefinitions() { @@ -61,23 +71,27 @@ public function getPropertyDefinitions() { /** * Overrides \Drupal\Core\Entity\Field\FieldItemBase::setValue(). */ - public function setValue($values) { + public function setValue($values, $notify = TRUE) { // Treat the values as property value of the entity field, if no array // is given. That way we support setting the field by entity ID or object. if (!is_array($values)) { $values = array('entity' => $values); } + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->handleChanges($this->name, $this, $values); + } // Entity is computed out of the ID, so we only need to update the ID. Only // set the entity field if no ID is given. if (isset($values['target_id'])) { - $this->properties['target_id']->setValue($values['target_id']); + $this->properties['target_id']->setValue($values['target_id'], FALSE); } elseif (isset($values['entity'])) { - $this->properties['entity']->setValue($values['entity']); + $this->properties['entity']->setValue($values['entity'], FALSE); } else { - $this->properties['entity']->setValue(NULL); + $this->properties['entity']->setValue(NULL, FALSE); } unset($values['entity'], $values['target_id']); if ($values) { @@ -86,13 +100,6 @@ public function setValue($values) { } /** - * Overrides \Drupal\Core\Entity\Field\FieldItemBase::__set(). - */ - public function __set($name, $value) { - parent::__set($name, $value); - } - - /** * Overrides \Drupal\Core\Entity\Field\FieldItemBase::__get(). */ public function __get($name) { @@ -109,11 +116,22 @@ public function get($property_name) { } /** + * Overrides \Drupal\Core\Entity\Field\FieldItemBase::__set(). + */ + public function __set($name, $value) { + // Support setting values via property objects, but take care in as the + // value of the 'entity' property is typed data also. + if ($value instanceof TypedDataInterface && !($value instanceof \Drupal\Core\Entity\EntityInterface)) { + $value = $value->getValue(); + } + $this->set($name, $value); + } + + /** * Implements \Drupal\Core\Entity\Field\FieldItemInterface::__isset(). */ public function __isset($property_name) { $property_name = ($property_name == 'value') ? 'target_id' : $property_name; return parent::__isset($property_name); } - } diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php index 97a5627..134e91d 100644 --- a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php +++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php @@ -9,7 +9,7 @@ use Drupal\Core\TypedData\AccessibleInterface; use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\Core\TypedData\ContextAwareTypedData; +use Drupal\Core\TypedData\TypedData; use ArrayIterator; use IteratorAggregate; use InvalidArgumentException; @@ -20,7 +20,7 @@ * Via this object translated entity fields may be read and updated in the same * way as untranslatable entity fields on the entity object. */ -class EntityTranslation extends ContextAwareTypedData implements IteratorAggregate, AccessibleInterface, ComplexDataInterface { +class EntityTranslation extends TypedData implements IteratorAggregate, AccessibleInterface, ComplexDataInterface { /** * The array of translated fields, each being an instance of @@ -71,7 +71,11 @@ public function getValue() { /** * Overrides \Drupal\Core\TypedData\TypedData::setValue(). */ - public function setValue($values) { + public function setValue($values, $notify = TRUE) { + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->handleChanges($this->name, $this, $values); + } $this->fields = $values; } diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php index fdd4d38..071ddb0 100644 --- a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php +++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php @@ -10,8 +10,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityNG; use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\Core\TypedData\ContextAwareInterface; -use Drupal\Core\TypedData\ContextAwareTypedData; +use Drupal\Core\TypedData\TypedData; use Drupal\Core\TypedData\TypedDataInterface; use ArrayIterator; use IteratorAggregate; @@ -37,7 +36,7 @@ * - id source: If used as computed property, the ID property used to load * the entity object. */ -class EntityWrapper extends ContextAwareTypedData implements IteratorAggregate, ComplexDataInterface { +class EntityWrapper extends TypedData implements IteratorAggregate, ComplexDataInterface { /** * The referenced entity type. @@ -61,9 +60,9 @@ class EntityWrapper extends ContextAwareTypedData implements IteratorAggregate, protected $newEntity; /** - * Overrides ContextAwareTypedData::__construct(). + * Overrides TypedData::__construct(). */ - public function __construct(array $definition, $name = NULL, ContextAwareInterface $parent = NULL) { + public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) { parent::__construct($definition, $name, $parent); $this->entityType = isset($this->definition['constraints']['EntityType']) ? $this->definition['constraints']['EntityType'] : NULL; } @@ -94,7 +93,7 @@ protected function getIdSource() { * * Both the entity ID and the entity object may be passed as value. */ - public function setValue($value) { + public function setValue($value, $notify = TRUE) { // Support passing in the entity object. If it's not yet saved we have // to store the whole entity such that it could be saved later on. if ($value instanceof EntityInterface && $value->isNew()) { @@ -134,9 +133,7 @@ public function getString() { * Implements \IteratorAggregate::getIterator(). */ public function getIterator() { - // @todo: Remove check for EntityNG once all entity types are converted. - $entity = $this->getValue(); - if ($entity && $entity instanceof EntityNG) { + if ($entity = $this->getValue()) { return $entity->getIterator(); } return new ArrayIterator(array()); @@ -215,4 +212,14 @@ public function setPropertyValues($values) { public function isEmpty() { return !$this->getValue(); } + + /** + * Implements \Drupal\Core\TypedData\ComplexDataInterface::handleChanges(). + */ + public function handleChanges($property_name, TypedDataInterface $property, $new_value) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->handleChanges($this->name, $property, $new_value); + } + } } diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php index 305fc1f..b39e9a5 100644 --- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php +++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Field\FieldInterface; use Drupal\user\Plugin\Core\Entity\User; -use Drupal\Core\TypedData\ContextAwareInterface; +use Drupal\Core\TypedData\TypedDataInterface; use Drupal\Core\TypedData\ItemList; /** @@ -34,9 +34,9 @@ class Field extends ItemList implements FieldInterface { protected $list = array(); /** - * Overrides ContextAwareTypedData::__construct(). + * Overrides TypedData::__construct(). */ - public function __construct(array $definition, $name = NULL, ContextAwareInterface $parent = NULL) { + public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) { parent::__construct($definition, $name, $parent); // Always initialize one empty item as most times a value for at least one // item will be present. That way prototypes created by diff --git a/core/lib/Drupal/Core/Plugin/Context/Context.php b/core/lib/Drupal/Core/Plugin/Context/Context.php index e6d680f..9055d12 100644 --- a/core/lib/Drupal/Core/Plugin/Context/Context.php +++ b/core/lib/Drupal/Core/Plugin/Context/Context.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Plugin\Context; use Drupal\Component\Plugin\Context\Context as ComponentContext; +use Drupal\Core\Entity\Field\Type\EntityWrapper; use Drupal\Core\TypedData\ComplexDataInterface; use Drupal\Core\TypedData\ListInterface; use Drupal\Core\TypedData\TypedDataInterface; @@ -36,7 +37,7 @@ public function getContextValue() { $is_complex = $typed_value[0] instanceof ComplexDataInterface; } // @todo We won't need the getType == entity check once #1868004 lands. - if ($typed_value instanceof TypedDataInterface && (!$is_complex || $typed_value->getType() == 'entity')) { + if ($typed_value instanceof TypedDataInterface && (!$is_complex || $typed_value instanceof EntityWrapper)) { return $typed_value->getValue(); } return $typed_value; diff --git a/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraintValidator.php b/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraintValidator.php index d649ccf..0eb0edb 100644 --- a/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraintValidator.php +++ b/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraintValidator.php @@ -19,7 +19,8 @@ class BundleConstraintValidator extends ConstraintValidator { * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate(). */ public function validate($typed_data, Constraint $constraint) { - $entity = isset($typed_data) ? $typed_data->getValue() : FALSE; + // If the entity is contained in a reference, unwrap it first. + $entity = isset($typed_data) && !($typed_data instanceof EntityInterface) ? $typed_data->getValue() : FALSE; if (!empty($entity) && !in_array($entity->bundle(), $constraint->getBundleOption())) { $this->context->addViolation($constraint->message, array('%bundle', implode(', ', $constraint->getBundleOption()))); diff --git a/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php b/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php index 1e1aba4..3020078 100644 --- a/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php +++ b/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Plugin\Validation\Constraint; +use Drupal\Core\Entity\EntityInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -19,7 +20,8 @@ class EntityTypeConstraintValidator extends ConstraintValidator { * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate(). */ public function validate($typed_data, Constraint $constraint) { - $entity = isset($typed_data) ? $typed_data->getValue() : FALSE; + // If the entity is contained in a reference, unwrap it first. + $entity = isset($typed_data) && !($typed_data instanceof EntityInterface) ? $typed_data->getValue() : FALSE; if (!empty($entity) && $entity->entityType() != $constraint->type) { $this->context->addViolation($constraint->message, array('%type' => $constraint->type)); diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php index 62f0363..132212a 100644 --- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php +++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php @@ -21,7 +21,7 @@ * When implementing this interface which extends Traversable, make sure to list * IteratorAggregate or Iterator before this interface in the implements clause. */ -interface ComplexDataInterface extends Traversable { +interface ComplexDataInterface extends Traversable, TypedDataInterface { /** * Gets a property object. @@ -120,4 +120,21 @@ public function getPropertyDefinitions(); * TRUE if the data structure is empty, FALSE otherwise. */ public function isEmpty(); + + /** + * Handle changes to a child property or item, if any. + * + * For example, if the 'value' property of a child 'body' property is changed + * the body property would be passed along with property path 'body.value'. + * + * @param $property_name + * The name of the property which has been changed. + * @param \Drupal\Core\TypedData\TypedDataInterface $changed + * The typed data object being the source of the changes. This might be the + * direct child property notifying the parent of changes, or any other + * descendent item. + * @param mixed $new_value + * The new value being set on the changed object. + */ + public function handleChanges($property_name, TypedDataInterface $changed, $new_value); } diff --git a/core/lib/Drupal/Core/TypedData/ContextAwareInterface.php b/core/lib/Drupal/Core/TypedData/ContextAwareInterface.php deleted file mode 100644 index d2b2d3b..0000000 --- a/core/lib/Drupal/Core/TypedData/ContextAwareInterface.php +++ /dev/null @@ -1,70 +0,0 @@ -definition = $definition; - $this->setContext($name, $parent); - } - - /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::setContext(). - */ - public function setContext($name = NULL, ContextAwareInterface $parent = NULL) { - $this->parent = $parent; - $this->name = $name; - } - - /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getName(). - */ - public function getName() { - return $this->name; - } - - /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getRoot(). - */ - public function getRoot() { - if (isset($this->parent)) { - return $this->parent->getRoot(); - } - // If no parent is set, this is the root of the data tree. - return $this; - } - - /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getPropertyPath(). - */ - public function getPropertyPath() { - if (isset($this->parent)) { - // The property path of this data object is the parent's path appended - // by this object's name. - $prefix = $this->parent->getPropertyPath(); - return (strlen($prefix) ? $prefix . '.' : '') . $this->name; - } - // If no parent is set, this is the root of the data tree. Thus the property - // path equals the name of this data object. - elseif (isset($this->name)) { - return $this->name; - } - return ''; - } - - /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getParent(). - * - * @return \Drupal\Core\Entity\Field\FieldInterface - */ - public function getParent() { - return $this->parent; - } -} diff --git a/core/lib/Drupal/Core/TypedData/ItemList.php b/core/lib/Drupal/Core/TypedData/ItemList.php index 3c6d01a..24a7d72 100644 --- a/core/lib/Drupal/Core/TypedData/ItemList.php +++ b/core/lib/Drupal/Core/TypedData/ItemList.php @@ -13,7 +13,7 @@ * This class can serve as list for any type of items. * Note: The class cannot be called "List" as list is a reserved PHP keyword. */ -class ItemList extends ContextAwareTypedData implements \IteratorAggregate, ListInterface { +class ItemList extends TypedData implements \IteratorAggregate, ListInterface { /** * Numerically indexed array items. @@ -203,9 +203,7 @@ public function __clone() { if (isset($this->list)) { foreach ($this->list as $delta => $item) { $this->list[$delta] = clone $item; - if ($item instanceof ContextAwareInterface) { - $this->list[$delta]->setContext($delta, $this); - } + $this->list[$delta]->setContext($delta, $this); } } } diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php index f6f906b..5bca254 100644 --- a/core/lib/Drupal/Core/TypedData/ListInterface.php +++ b/core/lib/Drupal/Core/TypedData/ListInterface.php @@ -20,7 +20,7 @@ * When implementing this interface which extends Traversable, make sure to list * IteratorAggregate or Iterator before this interface in the implements clause. */ -interface ListInterface extends ArrayAccess, Countable, Traversable { +interface ListInterface extends ArrayAccess, Countable, Traversable, TypedDataInterface { /** * Determines whether the list contains any non-empty items. diff --git a/core/lib/Drupal/Core/TypedData/Type/Binary.php b/core/lib/Drupal/Core/TypedData/Type/Binary.php index cf25c73..3e4c312 100644 --- a/core/lib/Drupal/Core/TypedData/Type/Binary.php +++ b/core/lib/Drupal/Core/TypedData/Type/Binary.php @@ -50,7 +50,11 @@ public function getValue() { * * Supports a PHP file resource or a (absolute) stream resource URI as value. */ - public function setValue($value) { + public function setValue($value, $notify = TRUE) { + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->handleChanges($this->name, $this, $value); + } if (!isset($value)) { $this->handle = NULL; $this->uri = NULL; diff --git a/core/lib/Drupal/Core/TypedData/Type/Date.php b/core/lib/Drupal/Core/TypedData/Type/Date.php index e326674..dcf661a 100644 --- a/core/lib/Drupal/Core/TypedData/Type/Date.php +++ b/core/lib/Drupal/Core/TypedData/Type/Date.php @@ -31,8 +31,11 @@ class Date extends TypedData { /** * Overrides TypedData::setValue(). */ - public function setValue($value) { - + public function setValue($value, $notify = TRUE) { + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->handleChanges($this->name, $this, $value); + } // Don't try to create a date from an empty value. // It would default to the current time. if (!isset($value)) { diff --git a/core/lib/Drupal/Core/TypedData/Type/Duration.php b/core/lib/Drupal/Core/TypedData/Type/Duration.php index fa539cf..f91fcb9 100644 --- a/core/lib/Drupal/Core/TypedData/Type/Duration.php +++ b/core/lib/Drupal/Core/TypedData/Type/Duration.php @@ -31,7 +31,11 @@ class Duration extends TypedData { /** * Overrides TypedData::setValue(). */ - public function setValue($value) { + public function setValue($value, $notify = TRUE) { + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->handleChanges($this->name, $this, $value); + } // Catch any exceptions thrown due to invalid values being passed. try { if ($value instanceof DateInterval || !isset($value)) { diff --git a/core/lib/Drupal/Core/TypedData/Type/Language.php b/core/lib/Drupal/Core/TypedData/Type/Language.php index 2226a7e..c16fbc4 100644 --- a/core/lib/Drupal/Core/TypedData/Type/Language.php +++ b/core/lib/Drupal/Core/TypedData/Type/Language.php @@ -9,7 +9,7 @@ use InvalidArgumentException; use Drupal\Core\Language\Language as LanguageObject; -use Drupal\Core\TypedData\ContextAwareTypedData; +use Drupal\Core\TypedData\TypedData; /** * Defines the 'language' data type. @@ -25,7 +25,7 @@ * - langcode source: If used as computed property, the langcode property used * to load the language object. */ -class Language extends ContextAwareTypedData { +class Language extends TypedData { /** * The language code of the language if no 'langcode source' is used. @@ -60,7 +60,11 @@ protected function getLanguageCodeSource() { * * Both the langcode and the language object may be passed as value. */ - public function setValue($value) { + public function setValue($value, $notify = TRUE) { + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->handleChanges($this->name, $this, $value); + } // Support passing language objects. if (is_object($value)) { $value = $value->langcode; diff --git a/core/lib/Drupal/Core/TypedData/Type/Map.php b/core/lib/Drupal/Core/TypedData/Type/Map.php index 5dd3674..2464c23 100644 --- a/core/lib/Drupal/Core/TypedData/Type/Map.php +++ b/core/lib/Drupal/Core/TypedData/Type/Map.php @@ -7,8 +7,9 @@ namespace Drupal\Core\TypedData\Type; -use Drupal\Core\TypedData\ContextAwareTypedData; +use Drupal\Core\TypedData\TypedData; use Drupal\Core\TypedData\ComplexDataInterface; +use Drupal\Core\TypedData\TypedDataInterface; /** * The "map" data type. @@ -20,7 +21,7 @@ * By default there is no metadata for contained properties. Extending classes * may want to override Map::getPropertyDefinitions() to define it. */ -class Map extends ContextAwareTypedData implements \IteratorAggregate, ComplexDataInterface { +class Map extends TypedData implements \IteratorAggregate, ComplexDataInterface { /** * An array of values for the contained properties. @@ -55,7 +56,10 @@ public function getPropertyDefinitions() { public function getValue() { // Update the values and return them. foreach ($this->properties as $name => $property) { - $this->values[$name] = $property->getValue(); + $definition = $property->getDefinition(); + if (empty($definition['computed'])) { + $this->values[$name] = $property->getValue(); + } } return $this->values; } @@ -66,12 +70,27 @@ public function getValue() { * @param array|null $values * An array of property values. */ - public function setValue($values) { + public function setValue($values, $notify = TRUE) { if (isset($values) && !is_array($values)) { throw new \InvalidArgumentException("Invalid values given. Values must be represented as an associative array."); } + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->handleChanges($this->name, $this, $value); + } $this->values = $values; - $this->properties = array(); + + // Update any existing property objects. + foreach ($this->properties as $name => $property) { + $definition = $property->getDefinition(); + if (empty($definition['computed'])) { + $value = NULL; + if (isset($values[$name])) { + $value = $values[$name]; + } + $property->setValue($value, FALSE); + } + } } /** @@ -90,11 +109,9 @@ public function getString() { * Implements \Drupal\Core\TypedData\ComplexDataInterface::get(). */ public function get($property_name) { - if (!$this->getPropertyDefinition($property_name)) { - throw new \InvalidArgumentException('Property ' . check_plain($property_name) . ' is unknown.'); - } if (!isset($this->properties[$property_name])) { - $this->properties[$property_name] = typed_data()->getPropertyInstance($this, $property_name, isset($this->values[$property_name]) ? $this->values[$property_name] : NULL); + // If the property is unknown, this will throw an exception. + $this->properties[$property_name] = typed_data()->getPropertyInstance($this, $property_name); } return $this->properties[$property_name]; } @@ -103,11 +120,11 @@ public function get($property_name) { * Implements \Drupal\Core\TypedData\ComplexDataInterface::set(). */ public function set($property_name, $value) { - if (isset($this->properties[$property_name])) { - $this->properties[$property_name]->setValue($value); + if ($this->getPropertyDefinition($property_name)) { + $this->get($property_name)->setValue($value); } else { - // Just the plain value, so it's possible to add a new entry to the map. + // Just set the plain value, which allows adding a new entry to the map. $this->values[$property_name] = $value; } } @@ -183,9 +200,17 @@ public function isEmpty() { public function __clone() { foreach ($this->getProperties() as $name => $property) { $this->properties[$name] = clone $property; - if ($property instanceof ContextAwareInterface) { - $this->properties[$name]->setContext($name, $this); - } + $this->properties[$name]->setContext($name, $this); + } + } + + /** + * Implements \Drupal\Core\TypedData\ComplexDataInterface::handleChanges(). + */ + public function handleChanges($property_name, TypedDataInterface $property, $new_value) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->handleChanges($this->name, $property, $new_value); } } } diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php index d6ad4be..1ea98c2 100644 --- a/core/lib/Drupal/Core/TypedData/TypedData.php +++ b/core/lib/Drupal/Core/TypedData/TypedData.php @@ -23,15 +23,36 @@ protected $definition; /** - * Constructs a TypedData object given its definition. + * The property name. + * + * @var string + */ + protected $name; + + /** + * The parent typed data object. + * + * @var \Drupal\Core\TypedData\TypedDataInterface + */ + protected $parent; + + /** + * Constructs a TypedData object given its definition and context. * * @param array $definition * The data definition. + * @param string $name + * (optional) The name of the created property, or NULL if it is the root + * of a typed data tree. Defaults to NULL. + * @param \Drupal\Core\TypedData\TypedDataInterface $parent + * (optional) The parent object of the data property, or NULL if it is the + * root of a typed data tree. Defaults to NULL. * * @see Drupal\Core\TypedData\TypedDataManager::create() */ - public function __construct(array $definition) { + public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) { $this->definition = $definition; + $this->setContext($name, $parent); } /** @@ -58,7 +79,11 @@ public function getValue() { /** * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue(). */ - public function setValue($value) { + public function setValue($value, $notify = TRUE) { + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->handleChanges($this->name, $this, $value); + } $this->value = $value; } @@ -84,4 +109,57 @@ public function validate() { // @todo: Add the typed data manager as proper dependency. return typed_data()->getValidator()->validate($this); } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::setContext(). + */ + public function setContext($name = NULL, TypedDataInterface $parent = NULL) { + $this->parent = $parent; + $this->name = $name; + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getName(). + */ + public function getName() { + return $this->name; + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getRoot(). + */ + public function getRoot() { + if (isset($this->parent)) { + return $this->parent->getRoot(); + } + // If no parent is set, this is the root of the data tree. + return $this; + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getPropertyPath(). + */ + public function getPropertyPath() { + if (isset($this->parent)) { + // The property path of this data object is the parent's path appended + // by this object's name. + $prefix = $this->parent->getPropertyPath(); + return (strlen($prefix) ? $prefix . '.' : '') . $this->name; + } + // If no parent is set, this is the root of the data tree. Thus the property + // path equals the name of this data object. + elseif (isset($this->name)) { + return $this->name; + } + return ''; + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getParent(). + * + * @return \Drupal\Core\Entity\Field\FieldInterface + */ + public function getParent() { + return $this->parent; + } } diff --git a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php index 0fcf36f..ac2297d 100644 --- a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php +++ b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php @@ -43,11 +43,15 @@ public function getValue(); * @param mixed|null $value * The value to set in the format as documented for the data type or NULL to * unset the data value. + * @param bool + * (optional) Whether to notify the parent object of the change. Defaults to + * TRUE. Usually parent objects should be notified unless the method is + * invoked by the parent itself. * * @throws \Drupal\Core\TypedData\ReadOnlyException * If the data is read-only. */ - public function setValue($value); + public function setValue($value, $notify = TRUE); /** * Returns a string representation of the data. @@ -73,4 +77,60 @@ public function getConstraints(); * succeeded. */ public function validate(); + + /** + * Returns the name of a property or item. + * + * @return string + * If the data is a property of some complex data, the name of the property. + * If the data is an item of a list, the name is the numeric position of the + * item in the list, starting with 0. Otherwise, NULL is returned. + */ + public function getName(); + + /** + * Returns the parent data structure; i.e. either complex data or a list. + * + * @return \Drupal\Core\TypedData\ComplexDataInterface|\Drupal\Core\TypedData\ListInterface + * The parent data structure, either complex data or a list; or NULL if this + * is the root of the typed data tree. + */ + public function getParent(); + + /** + * Returns the root of the typed data tree. + * + * Returns the root data for a tree of typed data objects; e.g. for an entity + * field item the root of the tree is its parent entity object. + * + * @return \Drupal\Core\TypedData\ComplexDataInterface|\Drupal\Core\TypedData\ListInterface + * The root data structure, either complex data or a list. + */ + public function getRoot(); + + /** + * Returns the property path of the data. + * + * The trail of property names relative to the root of the typed data tree, + * separated by dots; e.g. 'field_text.0.format'. + * + * @return string + * The property path relative to the root of the typed tree, or an empty + * string if this is the root. + */ + public function getPropertyPath(); + + /** + * Sets the context of a property or item via a context aware parent. + * + * This method is supposed to be called by the factory only. + * + * @param string $name + * (optional) The name of the property or the delta of the list item, + * or NULL if it is the root of a typed data tree. Defaults to NULL. + * @param \Drupal\Core\TypedData\TypedDataInterface $parent + * (optional) The parent object of the data property, or NULL if it is the + * root of a typed data tree. Defaults to NULL. + */ + public function setContext($name = NULL, TypedDataInterface $parent = NULL); } diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php index 0f8dc73..098be1f 100644 --- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php +++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php @@ -156,7 +156,7 @@ public function create(array $definition, $value = NULL, $name = NULL, $parent = * @param array $options * An array of options with the following keys: * - object: The parent typed data object, implementing the - * ContextAwareInterface and either the ListInterface or the + * TypedDataInterface and either the ListInterface or the * ComplexDataInterface. * - property: The name of the property to instantiate, or the delta of the * the list item to instantiate. @@ -187,8 +187,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\ContextAwareInterface $object - * The parent typed data object, implementing the ContextAwareInterface and + * @param \Drupal\Core\TypedData\TypedDataInterface $object + * The parent typed data object, implementing the TypedDataInterface and * either the ListInterface or the ComplexDataInterface. * @param string $property_name * The name of the property to instantiate, or the delta of an list item. @@ -204,8 +204,11 @@ public function getInstance(array $options) { * The new property instance. * * @see \Drupal\Core\TypedData\TypedDataManager::create() + * + * @todo: Add type-hinting to $object once entities implement the + * TypedDataInterface. */ - public function getPropertyInstance(ContextAwareInterface $object, $property_name, $value = NULL) { + public function getPropertyInstance($object, $property_name, $value = NULL) { if ($root = $object->getRoot()) { $key = $root->getType() . ':' . $object->getPropertyPath() . '.'; // If we are creating list items, we always use 0 in the key as all list @@ -244,9 +247,7 @@ public function getPropertyInstance(ContextAwareInterface $object, $property_nam // Clone from the prototype, then update the parent relationship and set the // data value if necessary. $property = clone $this->prototypes[$key]; - if ($property instanceof ContextAwareInterface) { - $property->setContext($property_name, $object); - } + $property->setContext($property_name, $object); if (isset($value)) { $property->setValue($value); } diff --git a/core/modules/comment/lib/Drupal/comment/FieldNewValue.php b/core/modules/comment/lib/Drupal/comment/FieldNewValue.php index 52875ca..9fc5cae 100644 --- a/core/modules/comment/lib/Drupal/comment/FieldNewValue.php +++ b/core/modules/comment/lib/Drupal/comment/FieldNewValue.php @@ -7,7 +7,7 @@ namespace Drupal\comment; -use Drupal\Core\TypedData\ContextAwareTypedData; +use Drupal\Core\TypedData\TypedData; use Drupal\Core\TypedData\ReadOnlyException; use InvalidArgumentException; @@ -16,7 +16,7 @@ * * @todo: Declare the list of allowed values once supported. */ -class FieldNewValue extends ContextAwareTypedData { +class FieldNewValue extends TypedData { /** * Implements \Drupal\Core\TypedData\TypedDataInterface::getValue(). @@ -36,7 +36,7 @@ public function getValue($langcode = NULL) { /** * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue(). */ - public function setValue($value) { + public function setValue($value, $notify = TRUE) { if (isset($value)) { throw new ReadOnlyException('Unable to set a computed property.'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php index 64366bc..c81c7f6 100644 --- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php @@ -352,7 +352,7 @@ public function testTypedDataMaps() { $properties = $typed_data->getProperties(); $this->assertEqual(array_keys($properties), array_keys($value)); - $this->assertIdentical($properties['one'], $typed_data->get('one')); + $this->assertIdentical($properties['one'], $typed_data->get('one'), 'Properties are identical.'); $typed_data->setPropertyValues(array('one' => 'eins')); $this->assertEqual($typed_data->get('one')->getValue(), 'eins'); diff --git a/core/modules/text/lib/Drupal/text/TextProcessed.php b/core/modules/text/lib/Drupal/text/TextProcessed.php index c73af6f..3a44722 100644 --- a/core/modules/text/lib/Drupal/text/TextProcessed.php +++ b/core/modules/text/lib/Drupal/text/TextProcessed.php @@ -7,8 +7,8 @@ namespace Drupal\text; -use Drupal\Core\TypedData\ContextAwareInterface; -use Drupal\Core\TypedData\ContextAwareTypedData; +use Drupal\Core\TypedData\TypedDataInterface; +use Drupal\Core\TypedData\TypedData; use Drupal\Core\TypedData\ReadOnlyException; use InvalidArgumentException; @@ -18,7 +18,7 @@ * Required settings (below the definition's 'settings' key) are: * - text source: The text property containing the to be processed text. */ -class TextProcessed extends ContextAwareTypedData { +class TextProcessed extends TypedData { /** * The text property. @@ -35,9 +35,9 @@ class TextProcessed extends ContextAwareTypedData { protected $format; /** - * Overrides ContextAwareTypedData::__construct(). + * Overrides TypedData::__construct(). */ - public function __construct(array $definition, $name = NULL, ContextAwareInterface $parent = NULL) { + public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) { parent::__construct($definition, $name, $parent); if (!isset($definition['settings']['text source'])) { @@ -46,9 +46,9 @@ public function __construct(array $definition, $name = NULL, ContextAwareInterfa } /** - * Overrides ContextAwareTypedData::setContext(). + * Overrides TypedData::setContext(). */ - public function setContext($name = NULL, ContextAwareInterface $parent = NULL) { + public function setContext($name = NULL, TypedDataInterface $parent = NULL) { parent::setContext($name, $parent); if (isset($parent)) { $this->text = $parent->get($this->definition['settings']['text source']); @@ -81,7 +81,7 @@ public function getValue($langcode = NULL) { /** * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue(). */ - public function setValue($value) { + public function setValue($value, $notify = TRUE) { if (isset($value)) { throw new ReadOnlyException('Unable to set a computed property.'); } diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php index 8c44bb0..83abb4b 100644 --- a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php +++ b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php @@ -9,7 +9,7 @@ use Drupal\views\ViewExecutable; use Drupal\Core\Database\Database; -use Drupal\Core\TypedData\ContextAwareInterface; +use Drupal\Core\TypedData\TypedDataInterface; use Drupal\views\Plugin\views\query\Sql; use Drupal\views\Plugin\Core\Entity\View; use Drupal\views\ViewStorageInterface; @@ -985,37 +985,93 @@ public function getOriginalEntity() { } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getName(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getType(). + */ + public function getType() { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getDefinition(). + */ + public function getDefinition() { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getValue(). + */ + public function getValue() { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue(). + */ + public function setValue($value) { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getString(). + */ + public function getString() { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getConstraints(). + */ + public function getConstraints() { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::validate(). + */ + public function validate() { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::getName(). */ public function getName() { return $this->__call(__FUNCTION__, func_get_args()); } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getRoot(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getRoot(). */ public function getRoot() { return $this->__call(__FUNCTION__, func_get_args()); } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getPropertyPath(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getPropertyPath(). */ public function getPropertyPath() { return $this->__call(__FUNCTION__, func_get_args()); } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getParent(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getParent(). */ public function getParent() { return $this->__call(__FUNCTION__, func_get_args()); } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::setContext(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::setContext(). + */ + public function setContext($name = NULL, TypedDataInterface $parent = NULL) { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::handleChanges(). */ - public function setContext($name = NULL, ContextAwareInterface $parent = NULL) { + public function handleChanges($property_name, TypedDataInterface $property, $new_value) { return $this->__call(__FUNCTION__, func_get_args()); } }