diff --git a/core/lib/Drupal/Core/TypedData/Validation/ComplexPropertyConstraintMetadata.php b/core/lib/Drupal/Core/TypedData/Validation/ComplexPropertyConstraintMetadata.php deleted file mode 100644 index d3c2aa7..0000000 --- a/core/lib/Drupal/Core/TypedData/Validation/ComplexPropertyConstraintMetadata.php +++ /dev/null @@ -1,88 +0,0 @@ -typedData = $typed_data; - $this->constraints = $constraints; - } - - /** - * {@inheritdoc} - */ - public function findConstraints($group) { - return $this->property ? $this->constraints : []; - } - - /** - * {@inheritdoc} - */ - public function getPropertyName() { - return 'value'; - } - - /** - * {@inheritdoc} - */ - public function getPropertyValue($container) { - return $container->value; - } - - /** - * {@inheritdoc} - */ - public function getConstrainedProperties() { - return $this->property ? [] : ['value']; - } - - /** - * {@inheritdoc} - */ - public function hasPropertyMetadata($property_name) { - return $this->property ? FALSE : ($property_name == 'value'); - } - - /** - * {@inheritdoc} - */ - public function getPropertyMetadata($property_name) { - $metadata = clone $this; - $metadata->property = TRUE; - return [$metadata]; - } - -} diff --git a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php index 6eba413..23f1945 100755 --- a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php +++ b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php @@ -43,4 +43,11 @@ public function getCascadingStrategy() { return CascadingStrategy::NONE; } + /** + * {@inheritdoc} + */ + public function getTypedData() { + return $this->typedData->getParent(); + } + } diff --git a/core/lib/Drupal/Core/TypedData/Validation/MetadataBase.php b/core/lib/Drupal/Core/TypedData/Validation/MetadataBase.php index 8380fa0..6584405 100755 --- a/core/lib/Drupal/Core/TypedData/Validation/MetadataBase.php +++ b/core/lib/Drupal/Core/TypedData/Validation/MetadataBase.php @@ -10,6 +10,9 @@ use Drupal\Core\TypedData\ComplexDataInterface; use Drupal\Core\TypedData\ListInterface; use Drupal\Core\TypedData\TypedDataInterface; +use Drupal\Core\Validation\Plugin\Validation\Constraint\ComplexDataConstraint; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\BadMethodCallException; use Symfony\Component\Validator\Mapping\ClassMetadataInterface; use Symfony\Component\Validator\Mapping\PropertyMetadataInterface; use Symfony\Component\Validator\Mapping\TraversalStrategy; @@ -21,6 +24,13 @@ abstract class MetadataBase implements ClassMetadataInterface, PropertyMetadataInterface { /** + * Array of constraints. + * + * @var \Symfony\Component\Validator\Constraint[] + */ + protected $constraints = []; + + /** * The typed data object the metadata is about. * * @var \Drupal\Core\TypedData\TypedDataInterface @@ -61,27 +71,24 @@ public function getTypedData() { * {@inheritdoc} */ public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) { - - // @todo: Do we have to care about groups? Symfony class metadata has - // $propagatedGroup. throw new BadMethodCallException('Not supported.'); - - $visitor->visit($this, $typed_data->getValue(), $group, $propertyPath); } - /** * {@inheritdoc} */ public function findConstraints($group) { - return $this->typedData->getConstraints(); + return $this->getConstraints(); } /** * {@inheritdoc} */ public function getConstraints() { - return $this->typedData->getConstraints(); + $constraints = $this->typedData->getConstraints(); + // Merge in property constraints from any ComplexDataConstraints applied to + // the parent. + return array_merge($constraints, $this->constraints); } /** @@ -142,4 +149,17 @@ public function getPropertyValue($container) { } } + /** + * Adds a property constraint. + * + * @param \Symfony\Component\Validator\Constraint $constraint + * The constraint to add. + * + * @return self + * Metadata instance. + */ + public function addPropertyConstraint(Constraint $constraint) { + $this->constraints[] = $constraint; + } + } diff --git a/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php index 7d271f8..2d52915 100755 --- a/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php +++ b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php @@ -24,11 +24,6 @@ class MetadataFactory implements MetadataFactoryInterface { * Some typed data object containing the value to validate. */ public function getMetadataFor($typed_data) { - if ($typed_data instanceof TypedDataPropertyValidationEnvelope) { - $constraints = $typed_data->getConstraints(); - $typed_data = $typed_data->getTypedData(); - return new ComplexPropertyConstraintMetadata($typed_data, $constraints); - } if (!$typed_data instanceof TypedDataInterface) { throw new \InvalidArgumentException('The passed value must be a typed data object.'); } diff --git a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php index 92dfcfd..6f9f5d7 100644 --- a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php +++ b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php @@ -9,6 +9,9 @@ use Drupal\Core\TypedData\ComplexDataInterface; use Drupal\Core\TypedData\ListInterface; +use Drupal\Core\TypedData\TypedDataInterface; +use Drupal\Core\Validation\Plugin\Validation\Constraint\ComplexDataConstraint; +use Symfony\Component\Validator\Exception\BadMethodCallException; use Symfony\Component\Validator\Mapping\CascadingStrategy; use Symfony\Component\Validator\Mapping\TraversalStrategy; use Symfony\Component\Validator\ValidationVisitorInterface; @@ -19,24 +22,24 @@ class PropertyContainerMetadata extends MetadataBase { /** - * {@inheritdoc} + * Array of property constraints. + * + * @var \Symfony\Component\Validator\Constraint[] */ - public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) { - throw new BadMethodCallException('Not supported.'); + protected $propertyConstraints = []; - // To let all constraints properly handle empty structures, pass on NULL - // if the data structure is empty. That way existing NotNull or NotBlank - // constraints work as expected. - if ($typed_data->isEmpty()) { - $typed_data = NULL; - } - $visitor->visit($this, $typed_data, $group, $propertyPath); - $pathPrefix = isset($propertyPath) && $propertyPath !== '' ? $propertyPath . '.' : ''; - - if ($typed_data) { - foreach ($typed_data as $name => $data) { - $metadata = $this->factory->getMetadataFor($data, $name); - $metadata->accept($visitor, $data, $group, $pathPrefix . $name); + /** + * {@inheritdoc} + */ + public function __construct(TypedDataInterface $typed_data, MetadataFactory $factory) { + parent::__construct($typed_data, $factory); + $constraints = $typed_data->getConstraints(); + foreach ($constraints as $constraint) { + if ($constraint instanceof ComplexDataConstraint) { + $this->propertyConstraints = $constraint->properties; + } + else { + $this->constraints[] = $constraint; } } } @@ -44,6 +47,27 @@ public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, /** * {@inheritdoc} */ + public function findConstraints($group) { + return $this->getConstraints(); + } + + /** + * {@inheritdoc} + */ + public function getConstraints() { + return $this->constraints; + } + + /** + * {@inheritdoc} + */ + public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) { + throw new BadMethodCallException('Not supported.'); + } + + /** + * {@inheritdoc} + */ public function hasPropertyMetadata($property_name) { try { $exists = (bool)$this->getPropertyMetadata($property_name); @@ -59,7 +83,16 @@ public function hasPropertyMetadata($property_name) { */ public function getPropertyMetadata($property_name) { if ($this->typedData instanceof ListInterface || $this->typedData instanceof ComplexDataInterface) { - return array($this->factory->getMetadataFor($this->typedData->get($property_name))); + /* @var \Drupal\Core\TypedData\Validation\MetadataBase $metadata */ + $metadata = $this->factory->getMetadataFor($this->typedData->get($property_name)); + // Merge in any applicable property constraints from + // ComplexDataConstraints that were applied to this object. + if (isset($this->propertyConstraints[$property_name])) { + foreach ($this->propertyConstraints[$property_name] as $constraint) { + $metadata->addPropertyConstraint($constraint); + } + } + return [$metadata]; } else { throw new \LogicException("There are no known properties."); diff --git a/core/lib/Drupal/Core/TypedData/Validation/TypedDataPropertyValidationEnvelope.php b/core/lib/Drupal/Core/TypedData/Validation/TypedDataPropertyValidationEnvelope.php deleted file mode 100644 index 7ddc074..0000000 --- a/core/lib/Drupal/Core/TypedData/Validation/TypedDataPropertyValidationEnvelope.php +++ /dev/null @@ -1,89 +0,0 @@ -value = $value; - $this->typedData = $typed_data; - $this->constraints = $constraints; - } - - /** - * Factory to create a new TypedDataPropertyValidationEnvelope. - * - * @param \Drupal\Core\TypedData\TypedDataInterface $typed_data - * Typed data for this property. - * @param \Symfony\Component\Validator\Constraint[] $constraints - * Array of constraints. - * @param mixed $value - * The value to validate. - * - * @return static - */ - public static function create($typed_data, array $constraints, $value) { - return new static($typed_data, $constraints, $value); - } - - /** - * Gets the typed data for this envelope. - * - * @return \Drupal\Core\TypedData\TypedDataInterface - * The wrapped typed data. - */ - public function getTypedData() { - return $this->typedData; - } - - /** - * Constraints for this envelope. - * - * @return \Symfony\Component\Validator\Constraint[] - * The constraints for this property. - */ - public function getConstraints() { - return $this->constraints; - } - -}