diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php index b7bddb3..9701df0 100644 --- a/core/lib/Drupal/Core/Config/Schema/Mapping.php +++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php @@ -53,15 +53,11 @@ public function get($property_name) { * Implements Drupal\Core\TypedData\ComplexDataInterface::set(). */ public function set($property_name, $value, $notify = TRUE) { - // Notify the parent of any changes to be made. - if ($notify && isset($this->parent)) { - $this->parent->onChange($this->name); - } // Set the data into the configuration array but behave according to the // interface specification when we've got a null value. if (isset($value)) { $this->value[$property_name] = $value; - return $this->get($property_name); + $property = $this->get($property_name); } else { // In these objects, when clearing the value, the property is gone. @@ -69,8 +65,12 @@ public function set($property_name, $value, $notify = TRUE) { $property = $this->get($property_name); unset($this->value[$property_name]); $property->setValue($value); - return $property; } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } + return $property; } /** diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index 54051a6..7deeb4e 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -10,7 +10,8 @@ use Drupal\Component\Uuid\Uuid; use Drupal\Core\Language\Language; use Drupal\Core\TypedData\TypedDataInterface; -use Drupal\user\UserInterface; +use Drupal\Core\TypedData\Annotation\DataType; +use Drupal\Core\Annotation\Translation; use IteratorAggregate; use Drupal\Core\Session\AccountInterface; @@ -21,6 +22,13 @@ * * This class can be used as-is by simple entity types. Entity types requiring * special handling can extend the class. + * + * @DataType( + * id = "entity", + * label = @Translation("Entity"), + * description = @Translation("All kind of entities, e.g. nodes, comments or users."), + * derivative = "\Drupal\Core\Entity\Plugin\TypedData\Deriver\EntityDeriver" + * ) */ class Entity implements IteratorAggregate, EntityInterface { @@ -421,62 +429,66 @@ public function getNGEntity() { } /** - * Implements \Drupal\Core\TypedData\TypedDataInterface::getType(). + * {@inheritdoc} */ public function getType() { - // @todo: Incorporate the entity type here by making entities proper - // typed data. See http://drupal.org/node/1868004. - return 'entity'; + // @todo: This does not make much sense, so remove once TypedDataInterface + // is removed. See https://drupal.org/node/2002138. + if ($this->bundle() != $this->entityType()) { + return 'entity:' . $this->entityType() . ':' . $this->bundle(); + } + return 'entity:' . $this->entityType(); } /** - * Implements \Drupal\Core\TypedData\TypedDataInterface::getDefinition(). + * {@inheritdoc} */ public function getDefinition() { + // @todo: This does not make much sense, so remove once TypedDataInterface + // is removed. See https://drupal.org/node/2002138. return array( 'type' => $this->getType() ); } /** - * Implements \Drupal\Core\TypedData\TypedDataInterface::getValue(). + * {@inheritdoc} */ public function getValue() { - // @todo: Implement by making entities proper typed data. See - // http://drupal.org/node/1868004. + // @todo: This does not make much sense, so remove once TypedDataInterface + // is removed. See https://drupal.org/node/2002138. + return $this->getPropertyValues(); } /** * 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. + // @todo: This does not make much sense, so remove once TypedDataInterface + // is removed. See https://drupal.org/node/2002138. + $this->setPropertyValues($value); } /** - * Implements \Drupal\Core\TypedData\TypedDataInterface::getString(). + * {@inheritdoc} */ public function getString() { - // @todo: Implement by making entities proper typed data. See - // http://drupal.org/node/1868004. + return $this->label(); } /** - * Implements \Drupal\Core\TypedData\TypedDataInterface::getConstraints(). + * {@inheritdoc} */ 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(). + * {@inheritdoc} */ public function validate() { - // @todo: Implement by making entities proper typed data. See - // http://drupal.org/node/1868004. + // @todo: Add the typed data manager as proper dependency. + return \Drupal::typedData()->getValidator()->validate($this); } /** diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php index b8fe82b..d116d01 100644 --- a/core/lib/Drupal/Core/Entity/EntityInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityInterface.php @@ -9,6 +9,7 @@ use Drupal\Core\TypedData\AccessibleInterface; use Drupal\Core\TypedData\ComplexDataInterface; +use Drupal\Core\TypedData\IdentifiableInterface; use Drupal\Core\TypedData\TranslatableInterface; /** @@ -27,16 +28,7 @@ * @see \Drupal\Core\TypedData\TypedDataManager * @see \Drupal\Core\Field\FieldInterface */ -interface EntityInterface extends ComplexDataInterface, AccessibleInterface, TranslatableInterface { - - /** - * Returns the entity identifier (the entity's machine name or numeric ID). - * - * @return - * The identifier of the entity, or NULL if the entity does not yet have - * an identifier. - */ - public function id(); +interface EntityInterface extends IdentifiableInterface, ComplexDataInterface, AccessibleInterface, TranslatableInterface { /** * Returns the entity UUID (Universally Unique Identifier). diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php index c3687a0..cc77f73 100644 --- a/core/lib/Drupal/Core/Entity/EntityNG.php +++ b/core/lib/Drupal/Core/Entity/EntityNG.php @@ -101,15 +101,6 @@ public function __construct(array $values, $entity_type, $bundle = FALSE) { } /** - * Gets the typed data type of the entity. - * - * @return string - */ - public function getType() { - return $this->entityType; - } - - /** * Initialize the object. Invoked upon construction and wake up. */ protected function init() { @@ -614,11 +605,4 @@ public function label($langcode = NULL) { return $label; } - /** - * {@inheritdoc} - */ - public function validate() { - // @todo: Add the typed data manager as proper dependency. - return \Drupal::typedData()->getValidator()->validate($this); - } } diff --git a/core/lib/Drupal/Core/Entity/Field/Field.php b/core/lib/Drupal/Core/Entity/Field/Field.php index 3948ddf..fdbc943 100644 --- a/core/lib/Drupal/Core/Entity/Field/Field.php +++ b/core/lib/Drupal/Core/Entity/Field/Field.php @@ -77,10 +77,6 @@ public function getValue($include_computed = FALSE) { * Overrides \Drupal\Core\TypedData\ItemList::setValue(). */ 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; } @@ -108,6 +104,10 @@ public function setValue($values, $notify = TRUE) { } } } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } } /** diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php index cb243d5..84eb341 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php @@ -49,10 +49,6 @@ public function setValue($values, $notify = TRUE) { $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) { @@ -63,6 +59,10 @@ public function setValue($values, $notify = TRUE) { $property->setValue($value, FALSE); unset($this->values[$name]); } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } } /** @@ -83,10 +83,6 @@ public function __get($name) { * {@inheritdoc} */ public function set($property_name, $value, $notify = TRUE) { - // Notify the parent of any changes to be made. - if ($notify && isset($this->parent)) { - $this->parent->onChange($this->name); - } // For defined properties there is either a property object or a plain // value that needs to be updated. if (isset($this->properties[$property_name])) { @@ -97,6 +93,10 @@ public function set($property_name, $value, $notify = TRUE) { else { $this->values[$property_name] = $value; } + // Directly notify ourselves. + if ($notify) { + $this->onChange($property_name); + } } /** @@ -135,7 +135,9 @@ public function onChange($property_name) { } // Remove the plain value, such that any further __get() calls go via the // updated property object. - unset($this->values[$property_name]); + if (isset($this->properties[$property_name])) { + unset($this->values[$property_name]); + } } /** diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php new file mode 100644 index 0000000..e13f002 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php @@ -0,0 +1,69 @@ +derivatives) && !empty($this->derivatives[$derivative_id])) { + return $this->derivatives[$derivative_id]; + } + $this->getDerivativeDefinitions($base_plugin_definition); + if (isset($this->derivatives[$derivative_id])) { + return $this->derivatives[$derivative_id]; + } + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions(array $base_plugin_definition) { + // Also keep the 'entity' defined as is. + $this->derivatives[''] = $base_plugin_definition; + // Add definitions for each entity type and bundle. + foreach (entity_get_info() as $entity_type => $info) { + $this->derivatives[$entity_type] = array( + 'label' => $info['label'], + 'class' => $info['class'], + 'constraints' => array('EntityType' => $entity_type), + ) + $base_plugin_definition; + + // Incorporate the bundles as entity:$entity_type:$bundle, if any. + foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) { + if ($bundle !== $entity_type) { + $this->derivatives[$entity_type . ':' . $bundle] = array( + 'label' => $bundle_info['label'], + 'class' => $info['class'], + 'constraints' => array( + 'EntityType' => $entity_type, + 'Bundle' => $bundle, + ), + ) + $base_plugin_definition; + } + } + } + return $this->derivatives; + } +} diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php new file mode 100644 index 0000000..1f85cc3 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReference.php @@ -0,0 +1,128 @@ + 'entity', + ); + if (isset($this->definition['constraints']['EntityType'])) { + $definition['type'] .= ':' . $this->definition['constraints']['EntityType']; + } + if (isset($this->definition['constraints']['Bundle']) && is_string($this->definition['constraints']['Bundle'])) { + $definition['type'] .= ':' . $this->definition['constraints']['Bundle']; + } + return $definition; + } + + /** + * {@inheritdoc} + */ + public function getTarget() { + if (!isset($this->target) && isset($this->id)) { + // If we have a valid reference, return the entity object which is typed + // data itself. + $this->target = entity_load($this->definition['constraints']['EntityType'], $this->id); + } + return $this->target; + } + + /** + * {@inheritdoc} + */ + public function getTargetIdentifier() { + if (isset($this->id)) { + return $this->id; + } + elseif ($entity = $this->getValue()) { + return $entity->id(); + } + } + + /** + * {@inheritdoc} + */ + public function getValue() { + // Entities are already typed data, so just return that. + return $this->getTarget(); + } + + /** + * {@inheritdoc} + */ + public function setValue($value, $notify = TRUE) { + unset($this->target); + unset($this->id); + + // Both the entity ID and the entity object may be passed as value. + if (!isset($value) || $value instanceof EntityInterface) { + // Enusre we set no BCEntity. + if (isset($value)) { + $value = $value->getNGEntity(); + } + $this->target = $value; + } + elseif (!is_scalar($value) || empty($this->definition['constraints']['EntityType'])) { + throw new \InvalidArgumentException('Value is not a valid entity.'); + } + else { + $this->id = $value; + } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } + } + + /** + * {@inheritdoc} + */ + public function getString() { + if ($entity = $this->getValue()) { + return $entity->label(); + } + return ''; + } +} diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReferenceItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReferenceItem.php index 7f756ad..b0bd98d 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReferenceItem.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityReferenceItem.php @@ -13,10 +13,13 @@ use Drupal\Core\TypedData\TypedDataInterface; /** - * Defines the 'entity_reference' entity field item. + * Defines the 'entity_reference_item' entity field item. * - * Required settings (below the definition's 'settings' key) are: - * - target_type: The entity type to reference. + * Supported settings (below the definition's 'settings' key) are: + * - target_type: The entity type to reference. Required. + * - target_bundle: (optional): If set, restricts the entity bundles which may + * may be referenced. May be set to an single bundle, or to an array of + * allowed bundles. * * @DataType( * id = "entity_reference_field", @@ -40,11 +43,11 @@ class EntityReferenceItem extends FieldItemBase { * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions(). */ public function getPropertyDefinitions() { - // Definitions vary by entity type, so key them by entity type. - $target_type = $this->definition['settings']['target_type']; + // Definitions vary by settings, so key them accordingly. + $key = implode(',', $this->definition['settings']); - if (!isset(self::$propertyDefinitions[$target_type])) { - static::$propertyDefinitions[$target_type]['target_id'] = array( + if (!isset(self::$propertyDefinitions[$key])) { + static::$propertyDefinitions[$key]['target_id'] = array( // @todo: Lookup the entity type's ID data type and use it here. 'type' => 'integer', 'label' => t('Entity ID'), @@ -52,20 +55,22 @@ public function getPropertyDefinitions() { 'Range' => array('min' => 0), ), ); - static::$propertyDefinitions[$target_type]['entity'] = array( - 'type' => 'entity', + static::$propertyDefinitions[$key]['entity'] = array( + 'type' => 'entity_reference', 'constraints' => array( - 'EntityType' => $target_type, + 'EntityType' => $this->definition['settings']['target_type'], ), 'label' => t('Entity'), 'description' => t('The referenced entity'), // The entity object is computed out of the entity ID. 'computed' => TRUE, 'read-only' => FALSE, - 'settings' => array('id source' => 'target_id'), ); + if (isset($this->definition['settings']['target_bundle'])) { + static::$propertyDefinitions[$key]['entity']['constraints']['Bundle'] = $this->definition['settings']['target_bundle']; + } } - return static::$propertyDefinitions[$target_type]; + return static::$propertyDefinitions[$key]; } /** @@ -96,12 +101,14 @@ public function __isset($property_name) { * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get(). */ public function setValue($values, $notify = TRUE) { - // Treat the values as value of the entity property, if no array is - // given as this handles entity IDs and objects. if (isset($values) && !is_array($values)) { - // Directly update the property instead of invoking the parent, so that - // the entity property can take care of updating the ID property. + // Directly update the property instead of invoking the parent, so it can + // handle objects and IDs. $this->properties['entity']->setValue($values, $notify); + // If notify was FALSE, ensure the target_id property gets synched. + if (!$notify) { + $this->set('target_id', $this->properties['entity']->getTargetIdentifier(), FALSE); + } } else { // Make sure that the 'entity' property gets set as 'target_id'. @@ -111,4 +118,18 @@ public function setValue($values, $notify = TRUE) { parent::setValue($values, $notify); } } + + /** + * {@inheritdoc} + */ + public function onChange($property_name) { + // Make sure that the target ID and the target property stay in sync. + if ($property_name == 'target_id') { + $this->properties['entity']->setValue($this->target_id, FALSE); + } + elseif ($property_name == 'entity') { + $this->set('target_id', $this->properties['entity']->getTargetIdentifier(), FALSE); + } + parent::onChange($property_name); + } } diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityTranslation.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityTranslation.php index 8a39126..bb48691 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityTranslation.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityTranslation.php @@ -81,11 +81,11 @@ public function getValue() { * Overrides \Drupal\Core\TypedData\TypedData::setValue(). */ public function setValue($values, $notify = TRUE) { - // Notify the parent of any changes to be made. + $this->fields = $values; + // Notify the parent of any changes. if ($notify && isset($this->parent)) { $this->parent->onChange($this->name); } - $this->fields = $values; } /** diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityWrapper.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityWrapper.php deleted file mode 100644 index e2561a9..0000000 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityWrapper.php +++ /dev/null @@ -1,227 +0,0 @@ -entityType = isset($this->definition['constraints']['EntityType']) ? $this->definition['constraints']['EntityType'] : NULL; - } - - /** - * Overrides \Drupal\Core\TypedData\TypedData::getValue(). - */ - public function getValue() { - if (isset($this->newEntity)) { - return $this->newEntity; - } - 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; - } - - /** - * Overrides \Drupal\Core\TypedData\TypedData::setValue(). - * - * Both the entity ID and the entity object may be passed as 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()) { - $this->newEntity = $value; - $this->entityType = $value->entityType(); - $value = 0; - } - elseif ($value instanceof EntityInterface) { - $this->entityType = $value->entityType(); - $value = $value->id(); - unset($this->newEntity); - } - elseif (isset($value) && !(is_scalar($value) && !empty($this->definition['constraints']['EntityType']))) { - throw new InvalidArgumentException('Value is not a valid entity.'); - } - // Update the 'id source' property, if given. - if (!empty($this->definition['settings']['id source'])) { - $this->parent->__set($this->definition['settings']['id source'], $value, $notify); - } - else { - // Notify the parent of any changes to be made. - if ($notify && isset($this->parent)) { - $this->parent->onChange($this->name); - } - $this->id = $value; - } - } - - /** - * Overrides \Drupal\Core\TypedData\TypedData::getString(). - */ - public function getString() { - if ($entity = $this->getValue()) { - return $entity->label(); - } - return ''; - } - - /** - * Implements \IteratorAggregate::getIterator(). - */ - public function getIterator() { - if ($entity = $this->getValue()) { - return $entity->getIterator(); - } - return new ArrayIterator(array()); - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::get(). - */ - public function get($property_name) { - // @todo: Allow navigating through the tree without data as well. - if ($entity = $this->getValue()) { - return $entity->get($property_name); - } - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::set(). - */ - public function set($property_name, $value, $notify = TRUE) { - $this->get($property_name)->setValue($value, FALSE); - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::getProperties(). - */ - public function getProperties($include_computed = FALSE) { - if ($entity = $this->getValue()) { - return $entity->getProperties($include_computed); - } - return array(); - } - - /** - * 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::getPropertyDefinitions(). - */ - public function getPropertyDefinitions() { - // @todo: Support getting definitions if multiple bundles are specified. - return \Drupal::entityManager()->getFieldDefinitionsByConstraints($this->entityType, $this->definition['constraints']); - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues(). - */ - public function getPropertyValues() { - if ($entity = $this->getValue()) { - return $entity->getPropertyValues(); - } - return array(); - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues(). - */ - public function setPropertyValues($values) { - if ($entity = $this->getValue()) { - $entity->setPropertyValues($values); - } - } - - /** - * Implements \Drupal\Core\TypedData\ComplexDataInterface::isEmpty(). - */ - 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/Plugin/DataType/LanguageItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageItem.php index 5338556..68526f5 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageItem.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageItem.php @@ -42,19 +42,18 @@ class LanguageItem extends FieldItemBase { * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions(). */ public function getPropertyDefinitions() { - if (!isset(static::$propertyDefinitions)) { static::$propertyDefinitions['value'] = array( 'type' => 'string', 'label' => t('Language code'), ); static::$propertyDefinitions['language'] = array( - 'type' => 'language', + 'type' => 'language_reference', 'label' => t('Language object'), + 'description' => t('The referenced language'), // The language object is retrieved via the language code. 'computed' => TRUE, 'read-only' => FALSE, - 'settings' => array('langcode source' => 'value'), ); } return static::$propertyDefinitions; @@ -71,6 +70,10 @@ public function setValue($values, $notify = TRUE) { // the language property can take care of updating the language code // property. $this->properties['language']->setValue($values, $notify); + // If notify was FALSE, ensure the value property gets synched. + if (!$notify) { + $this->set('value', $this->properties['language']->getTargetIdentifier(), FALSE); + } } else { // Make sure that the 'language' property gets set as 'value'. @@ -89,4 +92,18 @@ public function applyDefaultValue($notify = TRUE) { $this->setValue(array('value' => Language::LANGCODE_NOT_SPECIFIED), $notify); return $this; } + + /** + * {@inheritdoc} + */ + public function onChange($property_name) { + // Make sure that the value and the language property stay in sync. + if ($property_name == 'value') { + $this->properties['language']->setValue($this->value, FALSE); + } + elseif ($property_name == 'language') { + $this->set('value', $this->properties['language']->getTargetIdentifier(), FALSE); + } + parent::onChange($property_name); + } } diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageReference.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageReference.php new file mode 100644 index 0000000..8d143a7 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageReference.php @@ -0,0 +1,39 @@ + 'language', + ); + } +} diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php index f346129..2fb9e3a 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php @@ -10,6 +10,7 @@ use Drupal\Core\Database\Query\SelectInterface; use Drupal\Core\Entity\Query\QueryException; use Drupal\field\Plugin\Core\Entity\Field; +use Drupal\Core\Entity\Field\Type\EntityReference; /** * Adds tables and fields to the SQL entity query. @@ -140,13 +141,12 @@ function addField($field, $type, $langcode) { $propertyDefinitions = $entity->{$field['field_name']}->getPropertyDefinitions(); // If the column is not yet known, ie. the - // $node->field_image->entity case then use the id source as the - // column. - if (!$column && isset($propertyDefinitions[$relationship_specifier]['settings']['id source'])) { - // If this is a valid relationship, use the id source. - // Otherwise, the code executing the relationship will throw an - // exception anyways so no need to do it here. - $column = $propertyDefinitions[$relationship_specifier]['settings']['id source']; + // $node->field_image->entity case then use first property as + // column, i.e. target_id or fid. + // Otherwise, the code executing the relationship will throw an + // exception anyways so no need to do it here. + if (!$column && isset($propertyDefinitions[$relationship_specifier]) && $entity->{$field['field_name']}->get('entity') instanceof EntityReference) { + $column = current(array_keys($propertyDefinitions)); } // Prepare the next index prefix. $next_index_prefix = "$relationship_specifier.$column"; @@ -193,7 +193,7 @@ function addField($field, $type, $langcode) { $next_index_prefix = $relationship_specifier; } // Check for a valid relationship. - if (isset($propertyDefinitions[$relationship_specifier]['constraints']['EntityType']) && isset($propertyDefinitions[$relationship_specifier]['settings']['id source'])) { + if (isset($propertyDefinitions[$relationship_specifier]) && $entity->{$specifier}->get('entity') instanceof EntityReference) { // If it is, use the entity type. $entity_type = $propertyDefinitions[$relationship_specifier]['constraints']['EntityType']; $entity_info = entity_get_info($entity_type); diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php index 595064e..a681ffe 100644 --- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php +++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php @@ -128,7 +128,7 @@ public function isEmpty(); /** * React to changes to a child property. * - * Note that this is invoked before any changes are applied. + * Note that this is invoked after any changes have been applied. * * @param $property_name * The name of the property which is changed. diff --git a/core/lib/Drupal/Core/TypedData/DataDefinitionException.php b/core/lib/Drupal/Core/TypedData/DataDefinitionException.php new file mode 100644 index 0000000..2a23315 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/DataDefinitionException.php @@ -0,0 +1,15 @@ +target; + } + + /** + * {@inheritdoc} + */ + public function getValue() { + if ($target = $this->getTarget()) { + return $target->getValue(); + } + } + + /** + * {@inheritdoc} + */ + public function setValue($value, $notify = TRUE) { + $this->target = \Drupal::typedData()->create($this->getTargetDefinition(), $value); + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } + } + + /** + * {@inheritdoc} + */ + public function getString() { + return (string) $this->getType() . ':' . $this->getTargetIdentifier(); + } + + /** + * {@inheritdoc} + */ + public function getTargetIdentifier() { + $target = $this->getTarget(); + return isset($target) ? $target->id() : NULL; + } +} diff --git a/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php b/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php new file mode 100644 index 0000000..8a7ac82 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php @@ -0,0 +1,39 @@ +parent)) { - $this->parent->onChange($this->name); - } if (!isset($values) || $values === array()) { $this->list = $values; } @@ -72,6 +68,10 @@ public function setValue($values, $notify = TRUE) { } } } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } } /** diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php index 2711cd4..451e1af 100644 --- a/core/lib/Drupal/Core/TypedData/ListInterface.php +++ b/core/lib/Drupal/Core/TypedData/ListInterface.php @@ -41,7 +41,7 @@ public function getItemDefinition(); /** * React to changes to a child item. * - * Note that this is invoked before any changes are applied. + * Note that this is invoked after any changes have been applied. * * @param $delta * The delta of the item which is changed. diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php index 05315cf..d41769c 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php @@ -58,10 +58,6 @@ public function getValue() { * Supports a PHP file resource or a (absolute) stream resource URI as 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; @@ -75,6 +71,10 @@ public function setValue($value, $notify = TRUE) { else { $this->handle = $value; } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } } /** diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Date.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Date.php index 96a7db8..4c1d9a3 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Date.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Date.php @@ -39,10 +39,6 @@ class Date extends TypedData { * Overrides TypedData::setValue(). */ 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)) { @@ -51,5 +47,9 @@ public function setValue($value, $notify = TRUE) { else { $this->value = $value instanceOf DrupalDateTime ? $value : new DrupalDateTime($value); } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } } } diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Duration.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Duration.php index 575a4c3..43c3330 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Duration.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Duration.php @@ -39,10 +39,6 @@ class Duration extends TypedData { * Overrides TypedData::setValue(). */ 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)) { @@ -68,6 +64,10 @@ public function setValue($value, $notify = TRUE) { // validation fail. $this->value = $e; } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } } /** diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Language.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Language.php index fdb7e59..87d0bdf 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Language.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Language.php @@ -11,6 +11,7 @@ use Drupal\Core\Annotation\Translation; use InvalidArgumentException; use Drupal\Core\Language\Language as LanguageObject; +use Drupal\Core\TypedData\IdentifiableInterface; use Drupal\Core\TypedData\TypedData; /** @@ -20,39 +21,36 @@ * \Drupal\Core\Language\Language. For setting the value the language object or * the language code as string may be passed. * - * Optionally, this class may be used as computed property, see the supported - * settings below. E.g., it is used as 'language' property of language items. - * - * Supported settings (below the definition's 'settings' key) are: - * - langcode source: If used as computed property, the langcode property used - * to load the language object. - * * @DataType( * id = "language", * label = @Translation("Language"), * description = @Translation("A language object.") * ) */ -class Language extends TypedData { +class Language extends TypedData implements IdentifiableInterface { /** - * The language code of the language if no 'langcode source' is used. + * The id of the language. * * @var string */ - protected $langcode; + protected $id; + + /** + * @var \Drupal\Core\Language + */ + protected $language; /** * Overrides TypedData::getValue(). + * + * @return \Drupal\Core\Language\Language|null */ public function getValue() { - if (!empty($this->definition['settings']['langcode source'])) { - $this->id = $this->parent->__get($this->definition['settings']['langcode source']); - } - if ($this->id) { - $language = language_load($this->id); - return $language ?: new LanguageObject(array('id' => $this->id)); + if (!isset($this->language) && $this->id) { + $this->language = language_load($this->id); } + return $this->language; } /** @@ -63,21 +61,19 @@ public function getValue() { public function setValue($value, $notify = TRUE) { // Support passing language objects. if (is_object($value)) { - $value = $value->id; + $this->id = $value->id; + $this->language = $value; } elseif (isset($value) && !is_scalar($value)) { throw new InvalidArgumentException('Value is no valid langcode or language object.'); } - // Update the 'langcode source' property, if given. - if (!empty($this->definition['settings']['langcode source'])) { - $this->parent->__set($this->definition['settings']['langcode source'], $value, $notify); - } else { - // Notify the parent of any changes to be made. - if ($notify && isset($this->parent)) { - $this->parent->onChange($this->name); - } $this->id = $value; + $this->language = NULL; + } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); } } @@ -88,4 +84,17 @@ public function getString() { $language = $this->getValue(); return $language ? $language->name : ''; } + + /** + * {@inheritdoc} + */ + public function id() { + if (isset($this->id)) { + return $this->id; + } + elseif (isset($this->language)) { + return $this->language->id; + } + } + } diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php index a033322..180003c 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php @@ -84,10 +84,6 @@ 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; // Update any existing property objects. @@ -98,6 +94,10 @@ public function setValue($values, $notify = TRUE) { } $property->setValue($value, FALSE); } + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } } /** @@ -131,16 +131,16 @@ public function get($property_name) { * Implements \Drupal\Core\TypedData\ComplexDataInterface::set(). */ public function set($property_name, $value, $notify = TRUE) { - // Notify the parent of any changes to be made. - if ($notify && isset($this->parent)) { - $this->parent->onChange($this->name); - } if ($this->getPropertyDefinition($property_name)) { - $this->get($property_name)->setValue($value); + $this->get($property_name)->setValue($value, $notify); } else { // Just set the plain value, which allows adding a new entry to the map. $this->values[$property_name] = $value; + // Directly notify ourselves. + if ($notify) { + $this->onChange($property_name, $value); + } } } diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php index 8bfa215..f54f8ed 100644 --- a/core/lib/Drupal/Core/TypedData/TypedData.php +++ b/core/lib/Drupal/Core/TypedData/TypedData.php @@ -97,11 +97,11 @@ public function getValue() { * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue(). */ public function setValue($value, $notify = TRUE) { - // Notify the parent of any changes to be made. + $this->value = $value; + // Notify the parent of any changes. if ($notify && isset($this->parent)) { $this->parent->onChange($this->name); } - $this->value = $value; } /** diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php index 7e72bb5..262c94f 100644 --- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php +++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php @@ -218,18 +218,16 @@ public function getInstance(array $options) { * @todo: Add type-hinting to $object once entities implement the * TypedDataInterface. */ - 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 - // items look the same. - $key .= is_numeric($property_name) ? 0 : $property_name; - } - else { - // Missing context, thus we cannot determine a unique key for prototyping. - // Fall back to create a new prototype on each call. - $key = FALSE; + public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) { + $definition = $object->getRoot()->getDefinition(); + $key = $definition['type']; + if (isset($definition['settings'])) { + $key .= ':' . implode(',', $definition['settings']); } + $key .= ':' . $object->getPropertyPath() . '.'; + // If we are creating list items, we always use 0 in the key as all list + // items look the same. + $key .= is_numeric($property_name) ? 0 : $property_name; // Make sure we have a prototype. Then, clone the prototype and set object // specific values, i.e. the value and the context. diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraint.php index f6ea289..ebd844b 100644 --- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraint.php +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraint.php @@ -20,7 +20,7 @@ * @Plugin( * id = "Bundle", * label = @Translation("Bundle", context = "Validation"), - * type = "entity" + * type = { "entity", "entity_reference" } * ) */ class BundleConstraint extends Constraint { diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraintValidator.php index a84251e..42f09ca 100644 --- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraintValidator.php +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraintValidator.php @@ -18,10 +18,7 @@ class BundleConstraintValidator extends ConstraintValidator { /** * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate(). */ - public function validate($typed_data, Constraint $constraint) { - // If the entity is contained in a reference, unwrap it first. - $entity = isset($typed_data) && !($typed_data instanceof EntityInterface) ? $typed_data->getValue() : FALSE; - + public function validate($entity, Constraint $constraint) { 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/Validation/Plugin/Validation/Constraint/EntityTypeConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraint.php index 1d38ae5..d480fa1 100644 --- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraint.php +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraint.php @@ -20,7 +20,7 @@ * @Plugin( * id = "EntityType", * label = @Translation("Entity type", context = "Validation"), - * type = "entity" + * type = { "entity", "entity_reference" } * ) */ class EntityTypeConstraint extends Constraint { diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php index 31c7cb2..304a85c 100644 --- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php @@ -19,9 +19,7 @@ class EntityTypeConstraintValidator extends ConstraintValidator { /** * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate(). */ - public function validate($typed_data, Constraint $constraint) { - // If the entity is contained in a reference, unwrap it first. - $entity = isset($typed_data) && !($typed_data instanceof EntityInterface) ? $typed_data->getValue() : FALSE; + public function validate($entity, Constraint $constraint) { if (!empty($entity) && $entity->entityType() != $constraint->type) { $this->context->addViolation($constraint->message, array('%type' => $constraint->type)); diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 951906f..f706969 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -1482,7 +1482,7 @@ function comment_preprocess_block(&$variables) { function comment_prepare_author(Comment $comment) { // The account has been pre-loaded by CommentRenderController::buildContent(). $account = $comment->uid->entity; - if (!$account) { + if (empty($account->uid->value)) { $account = entity_create('user', array('uid' => 0, 'name' => $comment->name->value, 'homepage' => $comment->homepage->value)); } return $account->getBCEntity(); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php index c55927f..b53ba51 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php @@ -97,6 +97,7 @@ function testContentTypeLanguageConfiguration() { $this->drupalPost("admin/structure/types/manage/{$type2->type}", $edit, t('Save content type')); $this->assertRaw(t('The content type %type has been updated.', array('%type' => $type2->name))); $this->drupalLogout(); + drupal_static_reset('language_list'); // Verify language selection is not present on the node add form. $this->drupalLogin($web_user); @@ -153,6 +154,7 @@ function testContentTypeDirLang() { $edit = array(); $edit['predefined_langcode'] = 'ar'; $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); + drupal_static_reset('language_list'); // Install Spanish language. $edit = array(); diff --git a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php index b914837..d0fbb8c 100644 --- a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php +++ b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php @@ -419,8 +419,10 @@ public function testBlockContextualLinks() { public function testMenuBundles() { $this->drupalLogin($this->big_user); $menu = $this->addCustomMenu(); + // Clear the entity info cache to ensure the static caches are rebuilt. + entity_info_cache_clear(); $bundles = entity_get_bundles('menu_link'); - $this->assertTrue($bundles[$menu->id()]); + $this->assertTrue(isset($bundles[$menu->id()])); $menus = menu_list_system_menus(); $menus[$menu->id()] = $menu->label(); ksort($menus); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 9d9d48f..c2ed96d 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -2164,7 +2164,7 @@ function node_access($op, $node, $account = NULL, $langcode = NULL) { // If no language code was provided, default to the node's langcode. if (empty($langcode)) { - $langcode = $node->langcode; + $langcode = $node->language()->id; // If the Language module is enabled, try to use the language from content // negotiation. if (module_exists('language')) { diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php index e8878be..f1ab1fd 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php @@ -339,7 +339,7 @@ protected function assertSave($entity_type) { public function testIntrospection() { // All entity variations have to have the same results. foreach (entity_test_entity_types() as $entity_type) { - $this->assertIntrospection($entity_type); + $this->checkIntrospection($entity_type); } } @@ -349,17 +349,11 @@ public function testIntrospection() { * @param string $entity_type * The entity type to run the tests with. */ - protected function assertIntrospection($entity_type) { - // Test getting metadata upfront, i.e. without having an entity object. - $definition = array( - 'type' => 'entity', - 'constraints' => array( - 'EntityType' => $entity_type, - ), - 'label' => 'Test entity', - ); - $wrapped_entity = $this->container->get('typed_data')->create($definition); - $definitions = $wrapped_entity->getPropertyDefinitions($definition); + protected function checkIntrospection($entity_type) { + // Test getting metadata upfront. + // @todo: Make this work without having to create entity objects. + $entity = entity_create($entity_type, array()); + $definitions = $entity->getPropertyDefinitions(); $this->assertEqual($definitions['name']['type'], 'string_field', $entity_type .': Name field found.'); $this->assertEqual($definitions['user_id']['type'], 'entity_reference_field', $entity_type .': User field found.'); $this->assertEqual($definitions['field_test_text']['type'], 'field_item:text', $entity_type .': Test-text-field field found.'); @@ -378,7 +372,7 @@ protected function assertIntrospection($entity_type) { $userref_properties = $entity->user_id->getPropertyDefinitions(); $this->assertEqual($userref_properties['target_id']['type'], 'integer', $entity_type .': Entity id property of the user found.'); - $this->assertEqual($userref_properties['entity']['type'], 'entity', $entity_type .': Entity reference property of the user found.'); + $this->assertEqual($userref_properties['entity']['type'], 'entity_reference', $entity_type .': Entity reference property of the user found.'); $textfield_properties = $entity->field_test_text->getPropertyDefinitions(); $this->assertEqual($textfield_properties['value']['type'], 'string', $entity_type .': String value property of the test-text field found.'); @@ -470,21 +464,12 @@ public function testDataStructureInterfaces() { */ protected function assertDataStructureInterfaces($entity_type) { $entity = $this->createTestEntity($entity_type); - $entity->save(); - $entity_definition = array( - 'type' => 'entity', - 'constraints' => array( - 'EntityType' => $entity_type, - ), - 'label' => 'Test entity', - ); - $wrapped_entity = $this->container->get('typed_data')->create($entity_definition, $entity); // Test using the whole tree of typed data by navigating through the tree of // contained properties and getting all contained strings, limited by a // certain depth. $strings = array(); - $this->getContainedStrings($wrapped_entity, 0, $strings); + $this->getContainedStrings($entity, 0, $strings); // @todo: Once the user entity has defined properties this should contain // the user name and other user entity strings as well. @@ -532,17 +517,20 @@ public function getContainedStrings(TypedDataInterface $wrapper, $depth, array & public function testEntityConstraintValidation() { $entity = $this->createTestEntity('entity_test'); $entity->save(); - $entity_definition = array( - 'type' => 'entity', - 'constraints' => array( - 'EntityType' => 'entity_test', + // Create a reference field item and let it reference the entity. + $definition = array( + 'type' => 'entity_reference_field', + 'settings' => array( + 'target_type' => 'entity_test', ), 'label' => 'Test entity', ); - $wrapped_entity = $this->container->get('typed_data')->create($entity_definition, $entity); + $reference_field_item = \Drupal::TypedData()->create($definition); + $reference = $reference_field_item->get('entity'); + $reference->setValue($entity); // Test validation the typed data object. - $violations = $wrapped_entity->validate(); + $violations = $reference->validate(); $this->assertEqual($violations->count(), 0); // Test validating an entity of the wrong type. @@ -552,30 +540,31 @@ public function testEntityConstraintValidation() { 'type' => 'page', 'uid' => $user->id(), )); - // @todo: EntityWrapper can only handle entities with an id. - $node->save(); - $wrapped_entity->setValue($node); - $violations = $wrapped_entity->validate(); + $reference->setValue($node); + $violations = $reference->validate(); $this->assertEqual($violations->count(), 1); // Test bundle validation. - $entity_definition = array( - 'type' => 'entity', - 'constraints' => array( - 'EntityType' => 'node', - 'Bundle' => 'article', + $definition = array( + 'type' => 'entity_reference_field', + 'settings' => array( + 'target_type' => 'node', + 'target_bundle' => 'article', ), - 'label' => 'Test node', ); - $wrapped_entity = $this->container->get('typed_data')->create($entity_definition, $node); - - $violations = $wrapped_entity->validate(); + $reference_field_item = \Drupal::TypedData()->create($definition); + $reference = $reference_field_item->get('entity'); + $reference->setValue($node); + $violations = $reference->validate(); $this->assertEqual($violations->count(), 1); - $node->type = 'article'; + $node = entity_create('node', array( + 'type' => 'article', + 'uid' => $user->id(), + )); $node->save(); - $wrapped_entity->setValue($node); - $violations = $wrapped_entity->validate(); + $reference->setValue($node); + $violations = $reference->validate(); $this->assertEqual($violations->count(), 0); }