diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php index b7bddb3ae63b9d44be82f9ce9c350e1fef53d6da..9701df0de0388abb3e0f7e9eb242dbdbd6e22437 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 7712bf1fb6d5717ac2ec552a9bb4fce82fb60291..33a62cee212de922b909ceb58dc87f783fe9c329 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -11,7 +11,6 @@ use Drupal\Core\Language\Language; use Drupal\Core\TypedData\TranslatableInterface; use Drupal\Core\TypedData\TypedDataInterface; -use Drupal\user\UserInterface; use IteratorAggregate; use Drupal\Core\Session\AccountInterface; @@ -425,62 +424,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/EntityBCDecorator.php b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php index 52d3f6f303408d4a50631617d070c6afe2507bc6..a3ffcb91a310cb86952c01614fbad924d99a2acd 100644 --- a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php +++ b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php @@ -184,6 +184,7 @@ public function __set($name, $value) { // out of sync. That way, the next field object instantiated by EntityNG // will hold the updated value. unset($this->decorated->fields[$name]); + $this->decorated->onChange($name); } /** diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php index cdb4cd39b2d6152416756161d6cc43a5c1b39751..e965b8ecf7d9604496f49f0e2d9f4230cc39b2d8 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 3d0c5c653fed26ac4946af6151395eb23069272c..a6768e8edf02e6bc56e96c47dc31f4a77b26da85 100644 --- a/core/lib/Drupal/Core/Entity/EntityNG.php +++ b/core/lib/Drupal/Core/Entity/EntityNG.php @@ -167,15 +167,6 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans $this->init(); } - /** - * 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. */ @@ -458,7 +449,7 @@ protected function getDefaultLanguage() { } if (empty($this->language)) { // Make sure we return a proper language object. - $this->language = new Language(array('id' => Language::LANGCODE_NOT_SPECIFIED)); + $this->language = new Language(array('id' => Language::LANGCODE_NOT_SPECIFIED, 'locked' => TRUE)); } } return $this->language; @@ -841,12 +832,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 3948ddf80f28996a421b6b2a7c347e2302380875..fdbc9432a7ded5036756ef0b3610897cf61078ab 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 cb243d532874d0d2144bc0f71734c8d072a0754b..84eb341ccd2510aa82e52bb88f9ece5625ade059 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 0000000000000000000000000000000000000000..67ebea6f11303cb2e512a2bb29d37deae5d1b975 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/EntityDeriver.php @@ -0,0 +1,67 @@ +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/FieldDataTypeDerivative.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php similarity index 83% rename from core/lib/Drupal/Core/Entity/Plugin/DataType/FieldDataTypeDerivative.php rename to core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php index 346b656a4f9d00b8dfc4d1c659b2eb4ec3b7c084..2a304df2cb3b0c4db82709dfb2d83acdd3ee5b02 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldDataTypeDerivative.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php @@ -2,17 +2,17 @@ /** * @file - * Contains \Drupal\Core\Entity\Plugin\DataType\FieldDataTypeDerivative. + * Contains \Drupal\Core\Entity\Plugin\DataType\Deriver\FieldItemDeriver. */ -namespace Drupal\Core\Entity\Plugin\DataType; +namespace Drupal\Core\Entity\Plugin\DataType\Deriver; use Drupal\Component\Plugin\Derivative\DerivativeInterface; /** * Provides data type plugins for each existing field type plugin. */ -class FieldDataTypeDerivative implements DerivativeInterface { +class FieldItemDeriver implements DerivativeInterface { /** * List of derivative definitions. diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php new file mode 100644 index 0000000000000000000000000000000000000000..99762322aa35c4d5bccacfe7d4fba17fed538536 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Entity.php @@ -0,0 +1,28 @@ + '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. The + // reference may also be unset by passing NULL as value. + if (!isset($value) || $value instanceof EntityInterface) { + // Ensure we reference a NG Entity object. + 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 7f756ada3507e9dbfd74fb7d5a18ae371a7f73d0..3e67f7e56035c78cbe4d45fdc22b471f2253dd27 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,12 @@ 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 entity type and bundle, so key them accordingly. + $key = $this->definition['settings']['target_type'] . ':'; + $key .= isset($this->definition['settings']['target_bundle']) ? $this->definition['settings']['target_bundle'] : ''; - if (!isset(self::$propertyDefinitions[$target_type])) { - static::$propertyDefinitions[$target_type]['target_id'] = array( + if (!isset(static::$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 +56,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 +102,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 +119,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/EntityWrapper.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/EntityWrapper.php deleted file mode 100644 index e2561a90c85c60eac7c122733ecea72aa93727c7..0000000000000000000000000000000000000000 --- 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/FieldItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php index 73309d94a9ad9dd58d7069678dd93eecbbe07b4e..71fa2642b611182c097c135ad0db4badfdb3dc91 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php +++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/FieldItem.php @@ -9,18 +9,20 @@ use Drupal\Core\TypedData\Annotation\DataType; use Drupal\Core\Annotation\Translation; -use Drupal\Component\Plugin\PluginBase; /** - * Defines the base plugin definition for field type typed data types. + * Defines the base plugin for deriving data types for field types. + * + * Note that the class only register the plugin, and is actually never used. + * \Drupal\Core\Entity\Field\FieldItemBase is available for use as base class. * * @DataType( * id = "field_item", * label = @Translation("Field item"), * list_class = "\Drupal\Core\Entity\Field\Field", - * derivative = "Drupal\Core\Entity\Plugin\DataType\FieldDataTypeDerivative" + * derivative = "Drupal\Core\Entity\Plugin\DataType\Deriver\FieldItemDeriver" * ) */ -class FieldItem extends PluginBase { +abstract class FieldItem { } diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageItem.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/LanguageItem.php index 5338556ebbcba855fba8475ed823980a8ac28023..68526f5230d7e1faf00146c3a615a0abb135bff1 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 0000000000000000000000000000000000000000..8d143a75e90acba51da40829dbd32521d702ce61 --- /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 f346129515e6c496f04c029f5f1a2cb6b5d65d6a..2f7156adb4ec276228241dbe642bc2517f8d7e15 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Entity\Query\Sql; use Drupal\Core\Database\Query\SelectInterface; +use Drupal\Core\Entity\Plugin\DataType\EntityReference; use Drupal\Core\Entity\Query\QueryException; use Drupal\field\Plugin\Core\Entity\Field; @@ -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 595064eb1d65098c7a994468e7ccffb670638b12..a681ffe938516ab87bcc89677e4b4df2d8035ae8 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 0000000000000000000000000000000000000000..2a233152839fc07704e9e8f75b02e0641711dc18 --- /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 0000000000000000000000000000000000000000..501f9d22c156aec7a7eb51d5409520aa0413749d --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php @@ -0,0 +1,38 @@ +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 2711cd46f3b7ebe1cb526446a559ade5acfe3d68..451e1af18a72ee7f3e9a03166bc19ae363227c07 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 908f040465e0956e401001ec6b8fb46ef3cf21fb..16ac5d67dc4305146f715a7e216055f589677c14 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php @@ -59,10 +59,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; @@ -76,6 +72,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/DateTimeIso8601.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php index 59bbe8abe5acd2435dc08bae0573541f27431e86..906b4eb5a80dd6dccc1f8259956d36fbd08ae191 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php @@ -36,8 +36,12 @@ public function getDateTime() { /** * {@inheritdoc} */ - public function setDateTime(DrupalDateTime $dateTime) { + public function setDateTime(DrupalDateTime $dateTime, $notify = TRUE) { $this->value = $dateTime->format('c'); + // 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/DurationIso8601.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/DurationIso8601.php index d83261ff9638151304b788933864bc048b516601..9e67026ced7408dfffd2d6458192fdbf29235e17 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/DurationIso8601.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/DurationIso8601.php @@ -37,10 +37,14 @@ public function getDuration() { /** * {@inheritdoc} */ - public function setDuration(\DateInterval $duration) { + public function setDuration(\DateInterval $duration, $notify = TRUE) { // Generate an ISO 8601 formatted string as supported by // DateInterval::__construct() and setValue(). $this->value = $duration->format('%rP%yY%mM%dDT%hH%mM%sS'); + // 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 fdb7e59f788f8b29a746858d1ac91b05d035e96c..87d0bdffae6592826d7e5ff053606ced702b04fe 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 a03332222d6b5adf0f8689caf7f6a2bd4919c6d7..180003c5038e02ba74fe74223ffb8e40461813ec 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/Plugin/DataType/TimeSpan.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/TimeSpan.php index b0ec72a063f95c4728b2f97e76f39c9c3b85776e..c91fcd2e7a9f433d97e6279c9d8b0f76edf4c064 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/TimeSpan.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/TimeSpan.php @@ -43,7 +43,7 @@ public function getDuration() { /** * {@inheritdoc} */ - public function setDuration(\DateInterval $duration) { + public function setDuration(\DateInterval $duration, $notify = TRUE) { // Note that this applies the assumption of 12 month's a 30 days and // each year having 365 days. There is no accurate conversion for time spans // exceeding a day. @@ -53,6 +53,11 @@ public function setDuration(\DateInterval $duration) { ($duration->h * 60 * 60) + ($duration->i * 60) + $duration->s; + + // 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/Timestamp.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php index 18c99dc281fa8ae82861c3615c96a19bdbc14906..c0cad0203448c30c2c6644a0526e1cff7cc1eddf 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php @@ -41,7 +41,11 @@ public function getDateTime() { /** * {@inheritdoc} */ - public function setDateTime(DrupalDateTime $dateTime) { + public function setDateTime(DrupalDateTime $dateTime, $notify = TRUE) { $this->value = $dateTime->getTimestamp(); + // Notify the parent of any changes. + if ($notify && isset($this->parent)) { + $this->parent->onChange($this->name); + } } } diff --git a/core/lib/Drupal/Core/TypedData/PrimitiveBase.php b/core/lib/Drupal/Core/TypedData/PrimitiveBase.php index 1ba9319a0b83956e443ad9552a57951db8b9d07f..dae6dbe24f7ee34dd47f77ceb2bab705cf146598 100644 --- a/core/lib/Drupal/Core/TypedData/PrimitiveBase.php +++ b/core/lib/Drupal/Core/TypedData/PrimitiveBase.php @@ -30,10 +30,10 @@ public function getValue() { * {@inheritdoc} */ 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/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php index 8bfa2153f3bfef3870b074c7bb0bf7394d957f1c..f54f8ed274bb4f4a4d44201ef32d95f8d3b692dd 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 f0e80341f82f9bccb74849efd8e2b1bbf4f0d423..5c48fb0350844eede9a1752badb942a5aabeb0a1 100644 --- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php +++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php @@ -219,18 +219,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 f6ea28986bb0d1ec5e7063e62b1ab3014cd902fc..ebd844bfbcee011d9de1b56c73926ead92b27b89 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 a84251eb2dcca874013645f8cdf46b06d94f370b..42f09cabefb81ef13223f6fb44ea7fcf4c4812d1 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 1d38ae52867c9960fe8f5b8452f89c1730aa2d52..d480fa1fd7175b6825025eeffdcda895d04537b8 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 31c7cb2fb8d803df95addb9e94f9853b38b59751..304a85c69f45d6de364410fbb2e7c5bb0759aa3d 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 604cb93b95616387091aeea4fd2e3728864737fe..8ba44d1f0976a0e4658b6c2ffe15f5552ebd60ad 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/content_translation/lib/Drupal/content_translation/Tests/Views/TranslationLinkTest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/Views/TranslationLinkTest.php index 1f5e4571f1ce38815dc74e4e067f702c69257828..b26b0a58ba44c4e073753734402371f8e84fd612 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/Views/TranslationLinkTest.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/Views/TranslationLinkTest.php @@ -46,6 +46,11 @@ function setUp() { parent::setUp(); + // Assign user 1 a language code so that the entity can be translated. + $user = user_load(1); + $user->langcode = 'en'; + $user->save(); + ViewTestData::importTestViews(get_class($this), array('content_translation_test_views')); } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php index 7229a9ab3a5ea7eeabf02cacee1e71c5de04decc..ff4472fb8b9d0741c004f2a36a3fe6a3c4fcb3a1 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php @@ -57,14 +57,15 @@ public function getInstance() { * {@inheritdoc} */ public function getPropertyDefinitions() { - // Definitions vary by entity type, so key them by entity type. - $target_type = $this->definition['settings']['target_type']; + // Definitions vary by entity type and bundle, so key them accordingly. + $key = $this->definition['settings']['target_type'] . ':'; + $key .= isset($this->definition['settings']['target_bundle']) ? $this->definition['settings']['target_bundle'] : ''; - if (!isset(self::$propertyDefinitions[$target_type])) { + if (!isset(static::$propertyDefinitions[$key])) { // Call the parent to define the target_id and entity properties. parent::getPropertyDefinitions(); - static::$propertyDefinitions[$target_type]['revision_id'] = array( + static::$propertyDefinitions[$key]['revision_id'] = array( // @todo: Lookup the entity type's ID data type and use it here. 'type' => 'integer', 'label' => t('Revision ID'), @@ -72,18 +73,18 @@ public function getPropertyDefinitions() { 'Range' => array('min' => 0), ), ); - static::$propertyDefinitions[$target_type]['label'] = array( + static::$propertyDefinitions[$key]['label'] = array( 'type' => 'string', 'label' => t('Label (auto-create)'), 'computed' => TRUE, ); - static::$propertyDefinitions[$target_type]['access'] = array( + static::$propertyDefinitions[$key]['access'] = array( 'type' => 'boolean', 'label' => t('Access'), 'computed' => TRUE, ); } - return static::$propertyDefinitions[$target_type]; + return static::$propertyDefinitions[$key]; } /** diff --git a/core/modules/file/lib/Drupal/file/Type/FileItem.php b/core/modules/file/lib/Drupal/file/Type/FileItem.php index 9b2cc4cb96f901ca00d13659c9c11ffe3d37be26..c8f1a9ca9bc6f4289f296af0973d131a6d140d8d 100644 --- a/core/modules/file/lib/Drupal/file/Type/FileItem.php +++ b/core/modules/file/lib/Drupal/file/Type/FileItem.php @@ -28,20 +28,23 @@ class FileItem extends ConfigEntityReferenceItemBase { */ public function getPropertyDefinitions() { $this->definition['settings']['target_type'] = 'file'; + // Definitions vary by entity type and bundle, so key them accordingly. + $key = $this->definition['settings']['target_type'] . ':'; + $key .= isset($this->definition['settings']['target_bundle']) ? $this->definition['settings']['target_bundle'] : ''; - if (!isset(static::$propertyDefinitions)) { - static::$propertyDefinitions = parent::getPropertyDefinitions(); + if (!isset(static::$propertyDefinitions[$key])) { + static::$propertyDefinitions[$key] = parent::getPropertyDefinitions(); - static::$propertyDefinitions['display'] = array( + static::$propertyDefinitions[$key]['display'] = array( 'type' => 'boolean', 'label' => t('Flag to control whether this file should be displayed when viewing content.'), ); - static::$propertyDefinitions['description'] = array( + static::$propertyDefinitions[$key]['description'] = array( 'type' => 'string', 'label' => t('A description of the file.'), ); } - return static::$propertyDefinitions; + return static::$propertyDefinitions[$key]; } } diff --git a/core/modules/image/lib/Drupal/image/Type/ImageItem.php b/core/modules/image/lib/Drupal/image/Type/ImageItem.php index 7d441fd2c944951588079e9f815d1a7362261869..3b86fe4c11758a82d7a7dd12c95a57783d4ccce9 100644 --- a/core/modules/image/lib/Drupal/image/Type/ImageItem.php +++ b/core/modules/image/lib/Drupal/image/Type/ImageItem.php @@ -28,28 +28,31 @@ class ImageItem extends ConfigEntityReferenceItemBase { */ public function getPropertyDefinitions() { $this->definition['settings']['target_type'] = 'file'; + // Definitions vary by entity type and bundle, so key them accordingly. + $key = $this->definition['settings']['target_type'] . ':'; + $key .= isset($this->definition['settings']['target_bundle']) ? $this->definition['settings']['target_bundle'] : ''; - if (!isset(static::$propertyDefinitions)) { - static::$propertyDefinitions = parent::getPropertyDefinitions(); + if (!isset(static::$propertyDefinitions[$key])) { + static::$propertyDefinitions[$key] = parent::getPropertyDefinitions(); - static::$propertyDefinitions['alt'] = array( + static::$propertyDefinitions[$key]['alt'] = array( 'type' => 'string', 'label' => t("Alternative image text, for the image's 'alt' attribute."), ); - static::$propertyDefinitions['title'] = array( + static::$propertyDefinitions[$key]['title'] = array( 'type' => 'string', 'label' => t("Image title text, for the image's 'title' attribute."), ); - static::$propertyDefinitions['width'] = array( + static::$propertyDefinitions[$key]['width'] = array( 'type' => 'integer', 'label' => t('The width of the image in pixels.'), ); - static::$propertyDefinitions['height'] = array( + static::$propertyDefinitions[$key]['height'] = array( 'type' => 'integer', 'label' => t('The height of the image in pixels.'), ); } - return static::$propertyDefinitions; + return static::$propertyDefinitions[$key]; } } diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php index c55927fb104e4bcf315eb0c607f88677b8d1619f..b53ba51931442f8c04d455bc40a6c926f59df26a 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 b914837637a1c0cb59fe92afc9c9f50e1e0ca6f2..d0fbb8c8a5195a7a4fe442cc77c4ff30226b1e88 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 db4614ab631f262ae1bde4aa75d3c8bd768eb946..3a1acb4c23fff270b25e1343b85dcd0270d54d70 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 e8878be4f2eb7aa75ea2ef697634cc1ce5a880d9..7bc91079f61762bc56b3b1e45d0ce64871ea4f0e 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. @@ -526,23 +511,40 @@ public function getContainedStrings(TypedDataInterface $wrapper, $depth, array & } } + /** + * Makes sure data types are correctly derived for all entity types. + */ + public function testDataTypes() { + $types = \Drupal::typedData()->getDefinitions(); + foreach (entity_test_entity_types() as $entity_type) { + $this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registed.'); + } + // Check bundle types are provided as well. + entity_test_create_bundle('bundle'); + $types = \Drupal::typedData()->getDefinitions(); + $this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registed.'); + } + /** * Tests validation constraints provided by the Entity API. */ 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 +554,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); }