diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 2ece2b5..8da1dc7 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\String; use Drupal\Core\Entity\Plugin\DataType\EntityReference; +use Drupal\Core\Entity\Plugin\DataType\Entity as EntityTypedDataWrapper; use Drupal\Core\Entity\TypedData\EntityDataDefinition; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Session\AccountInterface; @@ -263,87 +264,9 @@ public function getDataDefinition() { /** * {@inheritdoc} */ - public function getValue() { - // @todo: This does not make much sense, so remove once TypedDataInterface - // is removed. See https://drupal.org/node/2002138. - return $this->toArray(); - } - - /** - * {@inheritdoc} - */ - public function setValue($value, $notify = TRUE) { - // @todo: This does not make much sense, so remove once TypedDataInterface - // is removed. See https://drupal.org/node/2002138. - foreach ($value as $field_name => $field_value) { - $this->set($field_name, $field_value, $notify); - } - } - - /** - * {@inheritdoc} - */ - public function getString() { - return (string) $this->label(); - } - - /** - * {@inheritdoc} - */ public function validate() { - return $this->typedDataManager()->getValidator()->validate($this); - } - - /** - * {@inheritdoc} - */ - public function applyDefaultValue($notify = TRUE) { - foreach ($this->getProperties() as $property) { - $property->applyDefaultValue(FALSE); - } - } - - /** - * {@inheritdoc} - */ - public function getConstraints() { - return array(); - } - - /** - * {@inheritdoc} - */ - public function getName() { - return NULL; - } - - /** - * {@inheritdoc} - */ - public function getRoot() { - return $this; - } - - /** - * {@inheritdoc} - */ - public function getPropertyPath() { - return ''; - } - - /** - * {@inheritdoc} - */ - public function getParent() { - return NULL; - } - - /** - * {@inheritdoc} - */ - 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. + // @todo: Add the typed data manager as proper dependency. + return \Drupal::typedDataManager()->getValidator()->validate(new EntityTypedDataWrapper($this)); } /** @@ -404,11 +327,11 @@ public function hasField($field_name) { /** * {@inheritdoc} */ - public function get($property_name) { - if (!isset($this->fields[$property_name][$this->activeLangcode])) { - return $this->getTranslatedField($property_name, $this->activeLangcode); + public function get($field_name) { + if (!isset($this->fields[$field_name][$this->activeLangcode])) { + return $this->getTranslatedField($field_name, $this->activeLangcode); } - return $this->fields[$property_name][$this->activeLangcode]; + return $this->fields[$field_name][$this->activeLangcode]; } /** @@ -443,7 +366,8 @@ protected function getTranslatedField($name, $langcode) { if (isset($this->values[$name][$langcode])) { $value = $this->values[$name][$langcode]; } - $field = \Drupal::typedDataManager()->getPropertyInstance($this, $name, $value); + $wrapper = new EntityTypedDataWrapper($this); + $field = \Drupal::typedDataManager()->getPropertyInstance($wrapper, $name, $value); if ($default) { // $this->defaultLangcode might not be set if we are initializing the // default language code cache, in which case there is no valid @@ -472,21 +396,21 @@ public function set($name, $value, $notify = TRUE) { /** * {@inheritdoc} */ - public function getProperties($include_computed = FALSE) { - $properties = array(); + public function getFields($include_computed = FALSE) { + $fields = array(); foreach ($this->getFieldDefinitions() as $name => $definition) { if ($include_computed || !$definition->isComputed()) { - $properties[$name] = $this->get($name); + $fields[$name] = $this->get($name); } } - return $properties; + return $fields; } /** * {@inheritdoc} */ public function getIterator() { - return new \ArrayIterator($this->getProperties()); + return new \ArrayIterator($this->getFields()); } /** @@ -516,7 +440,7 @@ public function getFieldDefinitions() { */ public function toArray() { $values = array(); - foreach ($this->getProperties() as $name => $property) { + foreach ($this->getFields() as $name => $property) { $values[$name] = $property->getValue(); } return $values; @@ -525,21 +449,6 @@ public function toArray() { /** * {@inheritdoc} */ - public function isEmpty() { - if (!$this->isNew()) { - return FALSE; - } - foreach ($this->getProperties() as $property) { - if ($property->getValue() !== NULL) { - return FALSE; - } - } - return TRUE; - } - - /** - * {@inheritdoc} - */ public function access($operation, AccountInterface $account = NULL) { if ($operation == 'create') { return $this->entityManager() @@ -623,8 +532,6 @@ public function onChange($name) { /** * {@inheritdoc} - * - * @return \Drupal\Core\Entity\ContentEntityInterface */ public function getTranslation($langcode) { // Ensure we always use the default language code when dealing with the @@ -805,15 +712,6 @@ public function getTranslationLanguages($include_default = TRUE) { } /** - * Overrides Entity::translations(). - * - * @todo: Remove once Entity::translations() gets removed. - */ - public function translations() { - return $this->getTranslationLanguages(FALSE); - } - - /** * Updates the original values with the interim changes. */ public function updateOriginalValues() { @@ -960,7 +858,7 @@ public function __clone() { } foreach ($values as $langcode => $items) { $this->fields[$name][$langcode] = clone $items; - $this->fields[$name][$langcode]->setContext($name, $this); + $this->fields[$name][$langcode]->setContext($name, new EntityTypedDataWrapper($this)); } } @@ -994,7 +892,7 @@ public function referencedEntities() { $referenced_entities = array(); // Gather a list of referenced entities. - foreach ($this->getProperties() as $field_items) { + foreach ($this->getFields() as $field_items) { foreach ($field_items as $field_item) { // Loop over all properties of a field item. foreach ($field_item->getProperties(TRUE) as $property) { diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php index f07936c..12d7ad8 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php @@ -28,7 +28,7 @@ * * @ingroup entity_api */ -interface ContentEntityInterface extends EntityInterface, RevisionableInterface, TranslatableInterface, ComplexDataInterface { +interface ContentEntityInterface extends EntityInterface, RevisionableInterface, TranslatableInterface { /** * Marks the translation identified by the given language code as existing. @@ -137,4 +137,58 @@ public function getFieldDefinitions(); */ public function toArray(); + /** + * Gets a field item list. + * + * @param $field_name + * The name of the field to get; e.g., 'title' or 'name'. + * + * @throws \InvalidArgumentException + * If an invalid field name is given. + * + * @return \Drupal\Core\Field\FieldItemListInterface + * The field item list, containing the field items. + */ + public function get($field_name); + + /** + * Sets a field value. + * + * @param $field_name + * The name of the property to set; e.g., 'title' or 'name'. + * @param $value + * The value to set, or NULL to unset the field. + * @param bool $notify + * (optional) Whether to notify the entity of the change. Defaults to + * TRUE. If the update stems from the entity, set it to FALSE to avoid + * being notified again. + * + * @throws \InvalidArgumentException + * If the specified field does not exist. + * + * @return $this + */ + public function set($field_name, $value, $notify = TRUE); + + /** + * Gets an array of field item lists. + * + * @param bool $include_computed + * If set to TRUE, computed fields are included. Defaults to FALSE. + * + * @return \Drupal\Core\Field\FieldItemListInterface[] + * An array of field item lists implementing, keyed by field name. + */ + public function getFields($include_computed = FALSE); + + /** + * React to changes to a child field. + * + * Note that this is invoked after any changes have been applied. + * + * @param $field_name + * The name of the field which is changed. + */ + public function onChange($field_name); + } diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php index dad9674..fdfccc5 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php @@ -387,7 +387,7 @@ protected function invokeTranslationHooks(ContentEntityInterface $entity) { protected function invokeFieldMethod($method, ContentEntityInterface $entity) { foreach (array_keys($entity->getTranslationLanguages()) as $langcode) { $translation = $entity->getTranslation($langcode); - foreach ($translation->getProperties(TRUE) as $field) { + foreach ($translation->getFields(TRUE) as $field) { $field->$method(); } } diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php index 5755c51..61ffd51 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php @@ -2,25 +2,107 @@ /** * @file - * Contains \Drupal\Core\Entity\Plugin\DataType\Entity. + * Contains \Drupal\Core\Entity\Plugin\DataType\EntityTypedDataWrapper. */ namespace Drupal\Core\Entity\Plugin\DataType; +use Drupal\Core\Entity\ContentEntityInterface; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\TypedData\ComplexDataInterface; +use Drupal\Core\TypedData\DataDefinitionInterface; +use Drupal\Core\TypedData\TypedData; +use Drupal\Core\TypedData\TypedDataInterface; /** * Defines the base plugin for deriving data types for entity types. * - * Note that the class only registers the plugin, and is actually never used. - * \Drupal\Core\Entity\Entity is available for use as base class. - * * @DataType( * id = "entity", * label = @Translation("Entity"), * description = @Translation("All kind of entities, e.g. nodes, comments or users."), - * deriver = "\Drupal\Core\Entity\Plugin\DataType\Deriver\EntityDeriver", + * derivative = "\Drupal\Core\Entity\Plugin\DataType\Deriver\EntityDeriver", * definition_class = "\Drupal\Core\Entity\TypedData\EntityDataDefinition" * ) */ -abstract class Entity { +class Entity extends TypedData implements \IteratorAggregate, ComplexDataInterface { + + /** + * @var \Drupal\Core\Entity\ContentEntityInterface + */ + protected $entity; + + /** + * {@inheritdoc} + */ + public function get($property_name) { + $this->entity->get($property_name); + } + + /** + * {@inheritdoc} + */ + public function set($property_name, $value, $notify = TRUE) { + return $this->entity->set($property_name, $value, $notify); + } + + /** + * {@inheritdoc} + */ + public function getProperties($include_computed = FALSE) { + return $this->entity->getFields($include_computed); + } + + /** + * {@inheritdoc} + */ + public function toArray() { + return $this->entity->toArray(); + } + + /** + * {@inheritdoc} + */ + public function isEmpty() { + // TODO: Implement isEmpty() method. + } + + /** + * {@inheritdoc} + */ + public function onChange($property_name) { + $this->entity->onChange($property_name); + } + + /** + * {@inheritdoc} + */ + public function getValue() { + return $this->entity; + } + + /** + * {@inheritdoc} + */ + public function setValue($value, $notify = TRUE) { + $this->entity = $value; + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } + } + + /** + * {@inheritdoc} + */ + public function validate() { + return $this->entity->validate(); + } + + /** + * {@inheritdoc} + */ + public function getIterator() { + return new \ArrayIterator($this->entity->getFields()); + } } diff --git a/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php b/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php index c2268c8..137da72 100644 --- a/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php +++ b/core/lib/Drupal/Core/Entity/TypedData/EntityDataDefinition.php @@ -59,7 +59,7 @@ public function getPropertyDefinitions() { if ($entity_type_id = $this->getEntityTypeId()) { // Return an empty array for entity types that don't support typed data. $entity_type_class = \Drupal::entityManager()->getDefinition($entity_type_id)->getClass(); - if (!in_array('Drupal\Core\TypedData\TypedDataInterface', class_implements($entity_type_class))) { + if (!in_array('Drupal\Core\Entity\ContentEntityInterface', class_implements($entity_type_class))) { $this->propertyDefinitions = array(); } else { diff --git a/core/lib/Drupal/Core/Field/FieldItemInterface.php b/core/lib/Drupal/Core/Field/FieldItemInterface.php index ee27996..718b203 100644 --- a/core/lib/Drupal/Core/Field/FieldItemInterface.php +++ b/core/lib/Drupal/Core/Field/FieldItemInterface.php @@ -89,7 +89,7 @@ public static function schema(FieldStorageDefinitionInterface $field_definition) /** * Gets the entity that field belongs to. * - * @return \Drupal\Core\Entity\EntityInterface + * @return \Drupal\Core\Entity\ContentEntityInterface * The entity object. */ public function getEntity(); diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php index 0c6559c..4227184 100644 --- a/core/lib/Drupal/Core/Field/FieldItemList.php +++ b/core/lib/Drupal/Core/Field/FieldItemList.php @@ -9,6 +9,7 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Entity\ContentEntityInterface; +use Drupal\Core\Entity\Plugin\DataType\Entity as EntityTypedDataWrapper; use Drupal\Core\Session\AccountInterface; use Drupal\Core\TypedData\DataDefinitionInterface; use Drupal\Core\TypedData\Plugin\DataType\ItemList; @@ -55,7 +56,7 @@ public function __construct(DataDefinitionInterface $definition, $name = NULL, T * {@inheritdoc} */ public function getEntity() { - return $this->getParent(); + return $this->getParent()->getValue(); } /** diff --git a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php index 999bfb7..58dadbd 100644 --- a/core/lib/Drupal/Core/TypedData/TranslatableInterface.php +++ b/core/lib/Drupal/Core/TypedData/TranslatableInterface.php @@ -46,7 +46,7 @@ public function getTranslationLanguages($include_default = TRUE); * LanguageInterface::LANGCODE_DEFAULT * to get the data in default language. * - * @return \Drupal\Core\TypedData\TypedDataInterface + * @return $this * A typed data object for the translated data. */ public function getTranslation($langcode); @@ -54,7 +54,7 @@ public function getTranslation($langcode); /** * Returns the translatable object referring to the original language. * - * @return \Drupal\Core\TypedData\TranslatableInterface + * @return $this * The translation object referring to the original language. */ public function getUntranslated(); @@ -79,7 +79,7 @@ public function hasTranslation($langcode); * (optional) An array of initial values to be assigned to the translatable * fields. Defaults to none. * - * @return \Drupal\Core\TypedData\TranslatableInterface + * @return $this */ public function addTranslation($langcode, array $values = array()); diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php index d433656..0f67644 100644 --- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php @@ -86,7 +86,7 @@ public function normalize($entity, $format = NULL, array $context = array()) { } } else { - $fields = $entity->getProperties(); + $fields = $entity->getFields(); } // Ignore the entity ID and revision ID. $exclude = array($entity->getEntityType()->getKey('id'), $entity->getEntityType()->getKey('revision')); diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php index d5ba85a..4673457 100644 --- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php @@ -73,6 +73,10 @@ public function normalize($field_item, $format = NULL, array $context = array()) // Normalize the target entity. $embedded = $this->serializer->normalize($target_entity, $format, $context); + // @todo Config entities currently can not be serialized, skip them. + if (empty($embedded['_links']['self'])) { + return array(); + } $link = $embedded['_links']['self']; // If the field is translatable, add the langcode to the link relation // object. This does not indicate the language of the target entity. diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserUniqueValidator.php b/core/modules/user/src/Plugin/Validation/Constraint/UserUniqueValidator.php index b98408a..92ff5cc 100644 --- a/core/modules/user/src/Plugin/Validation/Constraint/UserUniqueValidator.php +++ b/core/modules/user/src/Plugin/Validation/Constraint/UserUniqueValidator.php @@ -20,7 +20,7 @@ class UserUniqueValidator extends ConstraintValidator { */ public function validate($value, Constraint $constraint) { $field = $this->context->getMetadata()->getTypedData()->getParent(); - $uid = $field->getParent()->id(); + $uid = $field->getEntity()->id(); $value_taken = (bool) \Drupal::entityQuery('user') // The UID could be NULL, so we cast it to 0 in that case.