1<?php 2 3namespace Drupal\Core\TypedData\Validation; 4 5use Drupal\Core\TypedData\TypedDataInterface; 6use Drupal\Core\TypedData\TypedDataManagerInterface; 7use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; 8use Symfony\Component\Validator\Context\ExecutionContextFactoryInterface; 9use Symfony\Component\Validator\Context\ExecutionContextInterface; 10use Symfony\Component\Validator\Validator\ValidatorInterface; 11 12/** 13 * Defines a recursive validator for Typed Data. 14 * 15 * The difference to \Symfony\Component\Validator\Validator\RecursiveValidator 16 * is that we just allow to validate typed data objects. 17 */ 18class RecursiveValidator implements ValidatorInterface { 19 20 /** 21 * @var \Symfony\Component\Validator\Context\ExecutionContextFactoryInterface 22 */ 23 protected $contextFactory; 24 25 /** 26 * @var \Symfony\Component\Validator\ConstraintValidatorFactoryInterface 27 */ 28 protected $constraintValidatorFactory; 29 30 /** 31 * @var \Drupal\Core\TypedData\TypedDataManager 32 */ 33 protected $typedDataManager; 34 35 /** 36 * Creates a new validator. 37 * 38 * @param \Symfony\Component\Validator\Context\ExecutionContextFactoryInterface $context_factory 39 * The factory for creating new contexts. 40 * @param \Symfony\Component\Validator\ConstraintValidatorFactoryInterface $validator_factory 41 * The constraint validator factory. 42 * @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager 43 * The typed data manager. 44 */ 45 public function __construct(ExecutionContextFactoryInterface $context_factory, ConstraintValidatorFactoryInterface $validator_factory, TypedDataManagerInterface $typed_data_manager) { 46 $this->contextFactory = $context_factory; 47 $this->constraintValidatorFactory = $validator_factory; 48 $this->typedDataManager = $typed_data_manager; 49 } 50 51 /** 52 * {@inheritdoc} 53 */ 54 public function startContext($root = NULL) { 55 return new RecursiveContextualValidator($this->contextFactory->createContext($this, $root), $this, $this->constraintValidatorFactory, $this->typedDataManager); 56 } 57 58 /** 59 * {@inheritdoc} 60 */ 61 public function inContext(ExecutionContextInterface $context) { 62 return new RecursiveContextualValidator($context, $this, $this->constraintValidatorFactory, $this->typedDataManager); 63 } 64 65 /** 66 * {@inheritdoc} 67 * 68 * @param \Drupal\Core\TypedData\TypedDataInterface $typed_data 69 * A typed data object containing the value to validate. 70 */ 71 public function getMetadataFor($typed_data) { 72 if (!$typed_data instanceof TypedDataInterface) { 73 throw new \InvalidArgumentException('The passed value must be a typed data object.'); 74 } 75 return new TypedDataMetadata($typed_data); 76 } 77 78 /** 79 * {@inheritdoc} 80 */ 81 public function hasMetadataFor($value) { 82 return $value instanceof TypedDataInterface; 83 } 84 85 /** 86 * {@inheritdoc} 87 */ 88 public function validate($value, $constraints = NULL, $groups = NULL) { 89 return $this->startContext($value) 90 ->validate($value, $constraints, $groups) 91 ->getViolations(); 92 } 93 94 /** 95 * {@inheritdoc} 96 */ 97 public function validateProperty($object, $propertyName, $groups = NULL) { 98 return $this->startContext($object) 99 ->validateProperty($object, $propertyName, $groups) 100 ->getViolations(); 101 } 102 103 /** 104 * {@inheritdoc} 105 */ 106 public function validatePropertyValue($objectOrClass, $propertyName, $value, $groups = NULL) { 107 // Just passing a class name is not supported. 108 if (!is_object($objectOrClass)) { 109 throw new \LogicException('Typed data validation does not support passing the class name only.'); 110 } 111 return $this->startContext($objectOrClass) 112 ->validatePropertyValue($objectOrClass, $propertyName, $value, $groups) 113 ->getViolations(); 114 } 115 116} 117