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/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php index c09d9ad..e958f07 100644 --- a/core/lib/Drupal/Core/Config/Schema/Mapping.php +++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\TypedData\ComplexDataInterface; +use Drupal\Core\TypedData\TypedDataInterface; use \InvalidArgumentException; /** @@ -122,4 +123,14 @@ public function isEmpty() { return empty($this->value); } + /** + * Implements \Drupal\Core\TypedData\ComplexDataInterface::onChange(). + */ + public function onChange($property_name) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->onChange($this->name); + } + } + } diff --git a/core/lib/Drupal/Core/Config/Schema/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php index 0301b0c..a399583 100644 --- a/core/lib/Drupal/Core/Config/Schema/Sequence.php +++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php @@ -40,4 +40,13 @@ public function getItemDefinition() { return $this->definition['sequence'][0]; } + /** + * Implements \Drupal\Core\TypedData\ListInterface::onChange(). + */ + public function onChange($delta) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->onChange($this->name); + } + } } diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index 785c81c..84b2578 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::onChange(). + */ + public function onChange($property_name) { + // 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 08be87c..8a85621 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); } @@ -464,8 +513,14 @@ public function getExportProperties() { /** * Forwards the call to the decorated entity. */ + public function onChange($property_name) { + $this->decorated->onChange($property_name); + } + + /** + * Forwards the call to the decorated entity. + */ public function isTranslatable() { return $this->decorated->isTranslatable(); } - } diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php index b900490..8ac3c2a 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..01966c7 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,61 +19,20 @@ * * @see \Drupal\Core\Entity\Field\FieldItemInterface */ -abstract class FieldItemBase extends ContextAwareTypedData implements IteratorAggregate, FieldItemInterface { +abstract class FieldItemBase extends Map implements 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(). + * Overrides \Drupal\Core\TypedData\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); - - // 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(); + // Initialize computed properties by default, such that they get cloned + // with the whole item. foreach ($this->getPropertyDefinitions() as $name => $definition) { - if (empty($definition['computed'])) { + 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; } /** @@ -86,66 +41,56 @@ 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); + if (isset($values) && !is_array($values)) { + $keys = array_keys($this->getPropertyDefinitions()); $values = array($keys[0] => $values); } - + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } + $this->values = $values; + // Update any existing property objects. foreach ($this->properties as $name => $property) { + $value = NULL; if (isset($values[$name])) { - $property->setValue($values[$name]); - } - else { - $property->setValue(NULL); + $value = $values[$name]; } - unset($values[$name]); + $property->setValue($value, FALSE); + unset($this->values[$name]); } - // @todo: Throw an exception for invalid values once conversion is - // totally completed. - $this->extraValues = $values; } /** - * Overrides \Drupal\Core\TypedData\TypedData::getString(). + * Implements \Drupal\Core\Entity\Field\FieldItemInterface::__get(). */ - public function getString() { - $strings = array(); - foreach ($this->getProperties() as $property) { - $strings[] = $property->getString(); + public function __get($name) { + // Directly return the plain value, if we have any. + if (isset($this->values[$name])) { + return $this->values[$name]; } - 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.'); + elseif (isset($this->properties[$name])) { + return $this->properties[$name]->getValue(); } - return $this->properties[$property_name]; } /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::set(). + * Overrides \Drupal\Core\TypedData\Type\Map::set(). */ public function set($property_name, $value) { - $this->get($property_name)->setValue($value); - } - - /** - * Implements \Drupal\Core\Entity\Field\FieldItemInterface::__get(). - */ - public function __get($name) { - if (isset($this->properties[$name])) { - return $this->properties[$name]->getValue(); + if (isset($this->properties[$property_name])) { + $this->properties[$property_name]->setValue($value); } - // The property is unknown, so try to get it from the extra values. - elseif (isset($this->extraValues[$name])) { - return $this->extraValues[$name]; + else { + // Notify the parent of any changes to be made. + if (isset($this->parent)) { + $this->parent->onChange($this->name); + } + // Just set the plain value, which allows adding a new entry to the map. + $this->values[$property_name] = $value; } } @@ -153,105 +98,38 @@ public function __get($name) { * Implements \Drupal\Core\Entity\Field\FieldItemInterface::__set(). */ public function __set($name, $value) { - // Support setting values via property objects. - if ($value instanceof TypedDataInterface) { + // 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(); } - 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]) && $this->properties[$name]->getValue() !== NULL); } /** * 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); - } + $this->set($name, NULL); } /** - * Implements \IteratorAggregate::getIterator(). + * Overrides \Drupal\Core\TypedData\Map::onChange(). */ - 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; - } - - /** - * Magic method: Implements a deep clone. - */ - 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 onChange($property_name) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->onChange($this->name); } + // 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..17a57da 100644 --- a/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php +++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Entity\Field\Type; use Drupal\Core\Entity\Field\FieldItemBase; +use Drupal\Core\TypedData\TypedDataInterface; /** * Defines the 'entity_reference' entity field item. @@ -59,40 +60,6 @@ public function getPropertyDefinitions() { } /** - * Overrides \Drupal\Core\Entity\Field\FieldItemBase::setValue(). - */ - public function setValue($values) { - // 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); - } - - // 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']); - } - elseif (isset($values['entity'])) { - $this->properties['entity']->setValue($values['entity']); - } - else { - $this->properties['entity']->setValue(NULL); - } - unset($values['entity'], $values['target_id']); - if ($values) { - throw new \InvalidArgumentException('Property ' . key($values) . ' is unknown.'); - } - } - - /** - * 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) { @@ -116,4 +83,20 @@ public function __isset($property_name) { return parent::__isset($property_name); } + /** + * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get(). + */ + public function setValue($values, $notify = TRUE) { + // Treat the values as property value of the first property, if no array is + // given. + if (isset($values) && !is_array($values)) { + $keys = array_keys($this->getPropertyDefinitions()); + $values = array($keys[0] => $values); + } + // Make sure that the 'entity' property gets set as 'target_id'. + if (isset($values['target_id']) && !isset($values['entity'])) { + $values['entity'] = $values['target_id']; + } + parent::setValue($values, $notify); + } } diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php index 97a5627..4765a54 100644 --- a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php +++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php @@ -9,8 +9,9 @@ use Drupal\Core\TypedData\AccessibleInterface; use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\Core\TypedData\ContextAwareTypedData; +use Drupal\Core\TypedData\TypedData; use ArrayIterator; +use Drupal\Core\TypedData\TypedDataInterface; use IteratorAggregate; use InvalidArgumentException; @@ -20,7 +21,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 +72,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->onChange($this->name); + } $this->fields = $values; } @@ -193,6 +198,16 @@ public function isEmpty() { } /** + * Implements \Drupal\Core\TypedData\ComplexDataInterface::onChange(). + */ + public function onChange($property_name) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->onChange($this->name); + } + } + + /** * Implements \Drupal\Core\TypedData\AccessibleInterface::access(). */ public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) { diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php index fdd4d38..10492e6 100644 --- a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php +++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php @@ -8,10 +8,8 @@ namespace Drupal\Core\Entity\Field\Type; 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 +35,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 +59,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; } @@ -75,18 +73,10 @@ public function getValue() { if (isset($this->newEntity)) { return $this->newEntity; } - $source = $this->getIdSource(); - $id = $source ? $source->getValue() : $this->id; - return $id ? entity_load($this->entityType, $id) : NULL; - } - - /** - * Helper to get the typed data object holding the source entity ID. - * - * @return \Drupal\Core\TypedData\TypedDataInterface|FALSE - */ - protected function getIdSource() { - return !empty($this->definition['settings']['id source']) ? $this->parent->get($this->definition['settings']['id source']) : FALSE; + if (!empty($this->definition['settings']['id source'])) { + $this->id = $this->parent->__get($this->definition['settings']['id source']); + } + return $this->id ? entity_load($this->entityType, $this->id) : NULL; } /** @@ -94,7 +84,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()) { @@ -110,12 +100,15 @@ public function setValue($value) { elseif (isset($value) && !(is_scalar($value) && !empty($this->definition['constraints']['EntityType']))) { throw new InvalidArgumentException('Value is not a valid entity.'); } - // Now update the value in the source or the local id property. - $source = $this->getIdSource(); - if ($source) { - $source->setValue($value); + // Update the 'id source' property, if given. + if (!empty($this->definition['settings']['id source'])) { + $this->parent->__set($this->definition['settings']['id source'], $value); } else { + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } $this->id = $value; } } @@ -134,9 +127,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 +206,14 @@ public function setPropertyValues($values) { public function isEmpty() { return !$this->getValue(); } + + /** + * Implements \Drupal\Core\TypedData\ComplexDataInterface::onChange(). + */ + public function onChange($property_name) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->onChange($this->name); + } + } } diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php index 305fc1f..8c5451a 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 @@ -66,7 +66,11 @@ public function getValue() { /** * Overrides \Drupal\Core\TypedData\ItemList::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->onChange($this->name); + } if (!isset($values) || $values === array()) { $this->list = $values; } @@ -114,7 +118,7 @@ public function getPropertyDefinitions() { * Implements \Drupal\Core\Entity\Field\FieldInterface::__get(). */ public function __get($property_name) { - return $this->offsetGet(0)->get($property_name)->getValue(); + return $this->offsetGet(0)->__get($property_name); } /** diff --git a/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php b/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php index b3765b1..613f525 100644 --- a/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php +++ b/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Entity\Field\Type; use Drupal\Core\Entity\Field\FieldItemBase; -use InvalidArgumentException; +use Drupal\Core\TypedData\TypedDataInterface; /** * Defines the 'language_field' entity field item. @@ -47,30 +47,19 @@ public function getPropertyDefinitions() { } /** - * Overrides FieldItemBase::setValue(). + * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get(). */ - public function setValue($values) { - // Treat the values as property value of the object property, if no array - // is given. That way we support setting the field by language code or - // object. - if (!is_array($values)) { - $values = array('language' => $values); + public function setValue($values, $notify = TRUE) { + // Treat the values as property value of the first property, if no array is + // given. + if (isset($values) && !is_array($values)) { + $keys = array_keys($this->getPropertyDefinitions()); + $values = array($keys[0] => $values); } - - // Language is computed out of the langcode, so we only need to update the - // langcode. Only set the language property if no langcode is given. - if (!empty($values['value'])) { - $this->properties['value']->setValue($values['value']); - } - elseif (isset($values['language'])) { - $this->properties['language']->setValue($values['language']); - } - else { - $this->properties['language']->setValue(NULL); - } - unset($values['language'], $values['value']); - if ($values) { - throw new InvalidArgumentException('Property ' . key($values) . ' is unknown.'); + // Make sure that the 'language' property gets set as 'value'. + if (isset($values['value']) && !isset($values['language'])) { + $values['language'] = $values['value']; } + parent::setValue($values, $notify); } } 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..e3a540b 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,14 @@ public function getPropertyDefinitions(); * TRUE if the data structure is empty, FALSE otherwise. */ public function isEmpty(); + + /** + * React to changes to a child property. + * + * Note that this is invoked before any changes are applied. + * + * @param $property_name + * The name of the property which is changed. + */ + public function onChange($property_name); } 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..88af366 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. @@ -41,7 +41,11 @@ public function getValue() { * @param array|null $values * An array of values of the field items, or NULL to unset the field. */ - 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->onChange($this->name); + } if (!isset($values) || $values === array()) { $this->list = $values; } @@ -197,15 +201,23 @@ public function isEmpty() { } /** + * Implements \Drupal\Core\TypedData\ListInterface::onChange(). + */ + public function onChange($delta) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->onChange($this->name); + } + } + + /** * Magic method: Implements a deep clone. */ 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..2711cd4 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 TypedDataInterface, ArrayAccess, Countable, Traversable { /** * Determines whether the list contains any non-empty items. @@ -37,4 +37,14 @@ public function isEmpty(); * The data definition of contained items. */ public function getItemDefinition(); + + /** + * React to changes to a child item. + * + * Note that this is invoked before any changes are applied. + * + * @param $delta + * The delta of the item which is changed. + */ + public function onChange($delta); } diff --git a/core/lib/Drupal/Core/TypedData/Type/Binary.php b/core/lib/Drupal/Core/TypedData/Type/Binary.php index cf25c73..f9c4985 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->onChange($this->name); + } 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..c9b8c6b 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->onChange($this->name); + } // 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..52ba97d 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->onChange($this->name); + } // 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..0127314 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. @@ -38,29 +38,21 @@ class Language extends ContextAwareTypedData { * Overrides TypedData::getValue(). */ public function getValue() { - $source = $this->getLanguageCodeSource(); - $langcode = $source ? $source->getValue() : $this->langcode; - if ($langcode) { - $language = language_load($langcode); + if (!empty($this->definition['settings']['langcode source'])) { + $this->langcode = $this->parent->__get($this->definition['settings']['langcode source']); + } + if ($this->langcode) { + $language = language_load($this->langcode); return $language ?: new LanguageObject(array('langcode' => $this->langcode)); } } /** - * Helper to get the typed data object holding the source language code. - * - * @return \Drupal\Core\TypedData\TypedDataInterface|FALSE - */ - protected function getLanguageCodeSource() { - return !empty($this->definition['settings']['langcode source']) ? $this->parent->get($this->definition['settings']['langcode source']) : FALSE; - } - - /** * Overrides TypedData::setValue(). * * Both the langcode and the language object may be passed as value. */ - public function setValue($value) { + public function setValue($value, $notify = TRUE) { // Support passing language objects. if (is_object($value)) { $value = $value->langcode; @@ -68,12 +60,15 @@ public function setValue($value) { elseif (isset($value) && !is_scalar($value)) { throw new InvalidArgumentException('Value is no valid langcode or language object.'); } - // Now update the value in the source or the local langcode property. - $source = $this->getLanguageCodeSource(); - if ($source) { - $source->setValue($value); + // Update the 'langcode source' property, if given. + if (!empty($this->definition['settings']['langcode source'])) { + $this->parent->__set($this->definition['settings']['langcode source'], $value); } else { + // Notify the parent of any changes to be made. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } $this->langcode = $value; } } diff --git a/core/lib/Drupal/Core/TypedData/Type/Map.php b/core/lib/Drupal/Core/TypedData/Type/Map.php index 5dd3674..338ff8e 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,14 @@ 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'])) { + $value = $property->getValue(); + // Only write NULL values if the whole map is not NULL. + if (isset($this->values) || isset($value)) { + $this->values[$name] = $value; + } + } } return $this->values; } @@ -66,12 +74,24 @@ 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->onChange($this->name); + } $this->values = $values; - $this->properties = array(); + + // Update any existing property objects. + foreach ($this->properties as $name => $property) { + $value = NULL; + if (isset($values[$name])) { + $value = $values[$name]; + } + $property->setValue($value, FALSE); + } } /** @@ -90,11 +110,13 @@ 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); + $value = NULL; + if (isset($this->values[$property_name])) { + $value = $this->values[$property_name]; + } + // If the property is unknown, this will throw an exception. + $this->properties[$property_name] = typed_data()->getPropertyInstance($this, $property_name, $value); } return $this->properties[$property_name]; } @@ -103,11 +125,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; } } @@ -169,11 +191,19 @@ public function getPropertyDefinition($name) { * Implements \Drupal\Core\TypedData\ComplexDataInterface::isEmpty(). */ public function isEmpty() { - foreach ($this->getProperties() as $property) { - if ($property->getValue() !== NULL) { + foreach ($this->properties as $property) { + $definition = $property->getDefinition(); + if (empty($definition['computed']) && $property->getValue() !== NULL) { return FALSE; } } + if (isset($this->values)) { + foreach ($this->values as $name => $value) { + if (isset($value) && !isset($this->properties[$name])) { + return FALSE; + } + } + } return TRUE; } @@ -181,11 +211,19 @@ public function isEmpty() { * Magic method: Implements a deep clone. */ public function __clone() { - foreach ($this->getProperties() as $name => $property) { + foreach ($this->properties 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::onChange(). + */ + public function onChange($property_name) { + // Notify the parent of changes. + if (isset($this->parent)) { + $this->parent->onChange($this->name); } } } diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php index d6ad4be..e9558f3 100644 --- a/core/lib/Drupal/Core/TypedData/TypedData.php +++ b/core/lib/Drupal/Core/TypedData/TypedData.php @@ -23,15 +23,37 @@ 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->parent = $parent; + $this->name = $name; } /** @@ -58,7 +80,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->onChange($this->name); + } $this->value = $value; } @@ -84,4 +110,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..e598f9f 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. If a property is updated from a parent object, set it to FALSE to + * avoid being notified again. * * @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..195e32d 100644 --- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php +++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php @@ -145,7 +145,7 @@ public function createInstance($plugin_id, array $configuration, $name = NULL, $ public function create(array $definition, $value = NULL, $name = NULL, $parent = NULL) { $wrapper = $this->factory->createInstance($definition['type'], $definition, $name, $parent); if (isset($value)) { - $wrapper->setValue($value); + $wrapper->setValue($value, FALSE); } return $wrapper; } @@ -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,11 +247,9 @@ 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); + $property->setValue($value, FALSE); } return $property; } diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php index a5e5f15..70f3e5b 100644 --- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php +++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php @@ -158,7 +158,7 @@ public function form(array $form, array &$form_state, EntityInterface $comment) // Add internal comment properties. foreach (array('cid', 'pid', 'nid', 'uid', 'node_type', 'langcode') as $key) { - $key_name = key($comment->$key->offsetGet(0)->getProperties()); + $key_name = key($comment->$key->offsetGet(0)->getPropertyDefinitions()); $form[$key] = array('#type' => 'value', '#value' => $comment->$key->{$key_name}); } diff --git a/core/modules/comment/lib/Drupal/comment/FieldNewItem.php b/core/modules/comment/lib/Drupal/comment/FieldNewItem.php index 7ef4092..73fabe5 100644 --- a/core/modules/comment/lib/Drupal/comment/FieldNewItem.php +++ b/core/modules/comment/lib/Drupal/comment/FieldNewItem.php @@ -33,6 +33,7 @@ public function getPropertyDefinitions() { 'type' => 'integer', 'label' => t('Integer value'), 'class' => '\Drupal\comment\FieldNewValue', + 'computed' => TRUE, ); } return static::$propertyDefinitions; diff --git a/core/modules/comment/lib/Drupal/comment/FieldNewValue.php b/core/modules/comment/lib/Drupal/comment/FieldNewValue.php index 52875ca..8f9c392 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,12 +16,12 @@ * * @todo: Declare the list of allowed values once supported. */ -class FieldNewValue extends ContextAwareTypedData { +class FieldNewValue extends TypedData { /** * Implements \Drupal\Core\TypedData\TypedDataInterface::getValue(). */ - public function getValue($langcode = NULL) { + public function getValue() { if (!isset($this->value)) { if (!isset($this->parent)) { throw new InvalidArgumentException('Computed properties require context for computation.'); @@ -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/entity_reference/lib/Drupal/entity_reference/Type/ConfigurableEntityReferenceItem.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Type/ConfigurableEntityReferenceItem.php index eea88c7..cc7a3f4 100644 --- a/core/modules/entity_reference/lib/Drupal/entity_reference/Type/ConfigurableEntityReferenceItem.php +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Type/ConfigurableEntityReferenceItem.php @@ -62,25 +62,4 @@ public function getPropertyDefinitions() { return static::$propertyDefinitions[$target_type]; } - /** - * Overrides \Drupal\Core\Entity\Field\Type\EntityReferenceItem::setValue(). - */ - public function setValue($values) { - // 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); - } - - foreach (array('revision_id', 'access', 'label') as $property) { - if (array_key_exists($property, $values)) { - $this->properties[$property]->setValue($values[$property]); - unset($values[$property]); - } - } - - // Pass the remaining values through to the parent class. - parent::setValue($values); - } - } diff --git a/core/modules/file/lib/Drupal/file/Type/FileItem.php b/core/modules/file/lib/Drupal/file/Type/FileItem.php index cecce05..64815fe 100644 --- a/core/modules/file/lib/Drupal/file/Type/FileItem.php +++ b/core/modules/file/lib/Drupal/file/Type/FileItem.php @@ -8,6 +8,7 @@ namespace Drupal\file\Type; use Drupal\Core\Entity\Field\FieldItemBase; +use Drupal\Core\TypedData\TypedDataInterface; /** * Defines the 'file_field' entity field item. @@ -57,40 +58,19 @@ public function getPropertyDefinitions() { } /** - * Overrides \Drupal\Core\Entity\Field\FieldItemBase::setValue(). + * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get(). */ - public function setValue($values) { - // Treat the values as property value of the entity field, if no array - // is given. - if (!is_array($values)) { - $values = array('entity' => $values); + public function setValue($values, $notify = TRUE) { + // Treat the values as property value of the first property, if no array is + // given. + if (isset($values) && !is_array($values)) { + $keys = array_keys($this->getPropertyDefinitions()); + $values = array($keys[0] => $values); } - - if (isset($values['display'])) { - $this->properties['display']->setValue($values['display']); - } - if (isset($values['description'])) { - $this->properties['description']->setValue($values['description']); - } - - // 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['fid'])) { - $this->properties['fid']->setValue($values['fid']); - } - elseif (isset($values['entity'])) { - $this->properties['entity']->setValue($values['entity']); - } - else { - $this->properties['entity']->setValue(NULL); - } - unset($values['entity'], $values['fid']); - // @todo These properties are sometimes set due to being present in form - // values. Needs to be cleaned up somewhere. - unset($values['display'], $values['description'], $values['upload']); - if ($values) { - throw new \InvalidArgumentException('Property ' . key($values) . ' is unknown.'); + // Make sure that the 'entity' property gets set as 'fid'. + if (isset($values['fid']) && !isset($values['entity'])) { + $values['entity'] = $values['fid']; } + parent::setValue($values, $notify); } - } diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php index 2618d9a..a414155 100644 --- a/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php +++ b/core/modules/hal/lib/Drupal/hal/Normalizer/EntityNormalizer.php @@ -73,11 +73,16 @@ public function denormalize($data, $class, $format = NULL, array $context = arra throw new UnexpectedValueException('The type link relation must be specified.'); } - // Get language. - $langcode = isset($data['langcode']) ? $data['langcode'][0]['value'] : LANGUAGE_NOT_SPECIFIED; - // Create the entity. $typed_data_ids = $this->getTypedDataIds($data['_links']['type']); + // Figure out the language to use. + if (isset($data['langcode'])) { + $langcode = $data['langcode'][0]['value']; + } + else { + $langcode = language_get_default_langcode($typed_data_ids['entity_type'], $typed_data_ids['bundle']); + } + $entity = entity_create($typed_data_ids['entity_type'], array('langcode' => $langcode, 'type' => $typed_data_ids['bundle'])); // @todo Handle data in _links and _embedded, http://drupal.org/node/1880424 diff --git a/core/modules/image/lib/Drupal/image/Type/ImageItem.php b/core/modules/image/lib/Drupal/image/Type/ImageItem.php index 72bf09c..03d48c9 100644 --- a/core/modules/image/lib/Drupal/image/Type/ImageItem.php +++ b/core/modules/image/lib/Drupal/image/Type/ImageItem.php @@ -65,41 +65,19 @@ public function getPropertyDefinitions() { } /** - * Overrides \Drupal\Core\Entity\Field\FieldItemBase::setValue(). + * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get(). */ - public function setValue($values) { - // Treat the values as property value of the entity field, if no array - // is given. - if (!is_array($values)) { - $values = array('entity' => $values); + public function setValue($values, $notify = TRUE) { + // Treat the values as property value of the first property, if no array is + // given. + if (isset($values) && !is_array($values)) { + $keys = array_keys($this->getPropertyDefinitions()); + $values = array($keys[0] => $values); } - - foreach (array('alt', 'title', 'width', 'height') as $property) { - if (isset($values[$property])) { - $this->properties[$property]->setValue($values[$property]); - unset($values[$property]); - } - } - - // 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['fid'])) { - $this->properties['fid']->setValue($values['fid']); - } - elseif (isset($values['entity'])) { - $this->properties['entity']->setValue($values['entity']); - } - else { - $this->properties['entity']->setValue(NULL); - } - - unset($values['fid'], $values['entity']); - // @todo These properties are sometimes set due to being present in form - // values. Needs to be cleaned up somewhere. - unset($values['display'], $values['description'], $values['upload_button'], $values['remove_button'], $values['upload']); - if ($values) { - throw new \InvalidArgumentException('Property ' . key($values) . ' is unknown.'); + // Make sure that the 'entity' property gets set as 'fid'. + if (isset($values['fid']) && !isset($values['entity'])) { + $values['entity'] = $values['fid']; } + parent::setValue($values, $notify); } - } 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 880c720..6e72afe 100644 --- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php @@ -388,7 +388,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'); @@ -415,7 +415,7 @@ public function testTypedDataMaps() { $clone->setValue(array()); $typed_data->setValue(NULL); $this->assertNull($typed_data->getValue()); - $this->assertIdentical($clone->getValue(), array()); + $this->assertTrue(is_array($clone->getValue())); // Test accessing invalid properties. $typed_data->setValue($value); diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php index 7f7ab02..395fe88 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php @@ -8,6 +8,7 @@ namespace Drupal\taxonomy\Type; use Drupal\Core\Entity\Field\FieldItemBase; +use Drupal\Core\TypedData\TypedDataInterface; /** * Defines the 'taxonomy_term_reference' entity field item. @@ -49,30 +50,19 @@ public function getPropertyDefinitions() { } /** - * Overrides \Drupal\Core\Entity\Field\FieldItemBase::setValue(). + * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get(). */ - public function setValue($values) { - // Treat the values as property value of the entity field, if no array - // is given. - if (!is_array($values)) { - $values = array('entity' => $values); + public function setValue($values, $notify = TRUE) { + // Treat the values as property value of the first property, if no array is + // given. + if (isset($values) && !is_array($values)) { + $keys = array_keys($this->getPropertyDefinitions()); + $values = array($keys[0] => $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['tid'])) { - $this->properties['tid']->setValue($values['tid']); - } - elseif (isset($values['entity'])) { - $this->properties['entity']->setValue($values['entity']); - } - else { - $this->properties['entity']->setValue(NULL); - } - unset($values['entity'], $values['tid']); - if ($values) { - throw new \InvalidArgumentException('Property ' . key($values) . ' is unknown.'); + // Make sure that the 'entity' property gets set as 'tid'. + if (isset($values['tid']) && !isset($values['entity'])) { + $values['entity'] = $values['tid']; } + parent::setValue($values, $notify); } - } 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 e4bd339..4ea2375 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; @@ -994,37 +994,93 @@ public function isTranslatable() { } /** - * 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, $notify = TRUE) { + 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->storage->getName(); } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getRoot(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getRoot(). */ public function getRoot() { return $this->storage->getRoot(); } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getPropertyPath(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getPropertyPath(). */ public function getPropertyPath() { return $this->storage->getPropertyPath(); } /** - * Implements \Drupal\Core\TypedData\ContextAwareInterface::getParent(). + * Implements \Drupal\Core\TypedData\TypedDataInterface::getParent(). */ public function getParent() { return $this->storage->getParent(); } /** - * 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) { return $this->storage->setContext($name, $parent); } + + /** + * Implements \Drupal\Core\TypedData\TypedDataInterface::onChange(). + */ + public function onChange($property_name) { + $this->storage->onChange($property_name); + } }