diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraintValidator.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraintValidator.php index c6743c8..4501623 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraintValidator.php +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/BundleConstraintValidator.php @@ -19,22 +19,11 @@ class BundleConstraintValidator extends ConstraintValidator { /** * {@inheritdoc} */ - public function validate($entity_adapter, Constraint $constraint) { - if (!isset($entity_adapter)) { + public function validate($entity, Constraint $constraint) { + if (!isset($entity)) { return; } - // @todo The $entity_adapter parameter passed to this function should always - // be a typed data object, but due to a bug, the unwrapped entity is - // passed for the computed entity property of entity reference fields. - // Remove this after fixing that in https://www.drupal.org/node/2346373. - if (!$entity_adapter instanceof TypedDataInterface) { - $entity = $entity_adapter; - } - else { - $entity = $entity_adapter->getValue(); - } - if (!in_array($entity->bundle(), $constraint->getBundleOption())) { $this->context->addViolation($constraint->message, array('%bundle' => implode(', ', $constraint->getBundleOption()))); } diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php index 5138856..36476df 100644 --- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php @@ -17,24 +17,13 @@ class EntityTypeConstraintValidator extends ConstraintValidator { /** - * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate(). + * {@inheritdoc} */ - public function validate($entity_adapter, Constraint $constraint) { - if (!isset($entity_adapter)) { + public function validate($entity, Constraint $constraint) { + if (!isset($entity)) { return; } - // @todo The $entity_adapter parameter passed to this function should always - // be a typed data object, but due to a bug, the unwrapped entity is - // passed for the computed entity property of entity reference fields. - // Remove this after fixing that in https://www.drupal.org/node/2346373. - if (!$entity_adapter instanceof TypedDataInterface) { - $entity = $entity_adapter; - } - else { - $entity = $entity_adapter->getValue(); - } - /** @var $entity \Drupal\Core\Entity\EntityInterface */ if ($entity->getEntityTypeId() != $constraint->type) { $this->context->addViolation($constraint->message, array('%type' => $constraint->type)); diff --git a/core/lib/Drupal/Core/Field/FieldItemBase.php b/core/lib/Drupal/Core/Field/FieldItemBase.php index 1bb5486..c102d60 100644 --- a/core/lib/Drupal/Core/Field/FieldItemBase.php +++ b/core/lib/Drupal/Core/Field/FieldItemBase.php @@ -280,4 +280,13 @@ public static function onDependencyRemoval(FieldDefinitionInterface $field_defin return FALSE; } + /** + * {@inheritdoc} + */ + public function unwrap() { + // Field items directly implement the TypedDataInterface, so keep the object + // when Typed Data is unwrapped. + return $this; + } + } diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php index 930b4e1..d888ad3 100644 --- a/core/lib/Drupal/Core/Field/FieldItemList.php +++ b/core/lib/Drupal/Core/Field/FieldItemList.php @@ -370,4 +370,13 @@ protected function defaultValueWidget(FormStateInterface $form_state) { return $form_state->get('default_value_widget'); } + /** + * {@inheritdoc} + */ + public function unwrap() { + // Field item lists directly implement the TypedDataInterface, so keep the + // object when Typed Data is unwrapped. + return $this; + } + } diff --git a/core/lib/Drupal/Core/Plugin/Context/Context.php b/core/lib/Drupal/Core/Plugin/Context/Context.php index a0ad01f..4bcee6f 100644 --- a/core/lib/Drupal/Core/Plugin/Context/Context.php +++ b/core/lib/Drupal/Core/Plugin/Context/Context.php @@ -46,7 +46,7 @@ public function getContextValue() { } return NULL; } - return $this->contextData->getValue(); + return $this->contextData->unwrap(); } /** diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php index 7694db5..a223377 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php @@ -251,4 +251,11 @@ public function applyDefaultValue($notify = TRUE) { } return $this; } + + /** + * {@inheritdoc} + */ + public function unwrap() { + return $this; + } } diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php index 3216d21..7633d48 100644 --- a/core/lib/Drupal/Core/TypedData/TypedData.php +++ b/core/lib/Drupal/Core/TypedData/TypedData.php @@ -200,4 +200,12 @@ public function getPropertyPath() { public function getParent() { return $this->parent; } + + /** + * {@inheritdoc} + */ + public function unwrap() { + return $this->getValue(); + } + } diff --git a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php index 7b038e5..95d9c7f 100644 --- a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php +++ b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php @@ -164,4 +164,24 @@ public function getPropertyPath(); * root of a typed data tree. Defaults to NULL. */ public function setContext($name = NULL, TraversableTypedDataInterface $parent = NULL); + + /** + * Unwraps a data object if needed. + * + * Unwrapping removes any Typed Data objects wrapping the data in its regular + * representation; e.g., a string or an array. However, if data is regularly + * represented as an object and this objects directly implement the Typed + * Data API, there is no wrapping object to remove and the object itself is + * returned. + * + * This may be used to ensure data is in the right representation before it is + * passed on to other code components. E.g., when typed data is validated it + * gets unwrapped before it is passed on to constraint validators, such that + * entity objects get passed unwrapped and fields stay objects (which directly + * implemented TypedDataInterface). + * + * @return mixed + * The data, without any wrapping Typed Data object. + */ + public function unwrap(); } diff --git a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php index 73bfc8b..e5613f4 100644 --- a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php +++ b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php @@ -62,7 +62,7 @@ public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, // @todo: Do we have to care about groups? Symfony class metadata has // $propagatedGroup. - $visitor->visit($this, $typed_data->getValue(), $group, $propertyPath); + $visitor->visit($this, $typed_data->unwrap(), $group, $propertyPath); } /** @@ -89,7 +89,7 @@ public function getPropertyName() { * @return mixed The value of the property. */ public function getPropertyValue($container) { - return $this->typedData->getValue(); + return $this->typedData->unwrap(); } /** diff --git a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php index f5850eb..cbbbb2d 100644 --- a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php +++ b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php @@ -25,12 +25,16 @@ public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, // if the data structure is empty. That way existing NotNull or NotBlank // constraints work as expected. if ($typed_data->isEmpty()) { - $typed_data = NULL; + $data = NULL; } - $visitor->visit($this, $typed_data, $group, $propertyPath); + else { + $data = $typed_data->unwrap(); + } + $visitor->visit($this, $data, $group, $propertyPath); $pathPrefix = isset($propertyPath) && $propertyPath !== '' ? $propertyPath . '.' : ''; - if ($typed_data) { + // Only continue validating if the data is not empty. + if ($data) { foreach ($typed_data as $name => $data) { $metadata = $this->factory->getMetadataFor($data, $name); $metadata->accept($visitor, $data, $group, $pathPrefix . $name);