/vendor/symfony/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php

https://github.com/xbojer/gfw · PHP · 259 lines · 183 code · 47 blank · 29 comment · 27 complexity · 696301aa5b901248d0d5888281fe2af9 MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Form\Extension\Validator\Validator;
  11. use Symfony\Component\Form\FormInterface;
  12. use Symfony\Component\Form\FormValidatorInterface;
  13. use Symfony\Component\Form\FormError;
  14. use Symfony\Component\Form\Util\VirtualFormAwareIterator;
  15. use Symfony\Component\Form\Exception\FormException;
  16. use Symfony\Component\Validator\ValidatorInterface;
  17. use Symfony\Component\Validator\ExecutionContext;
  18. use Symfony\Component\Form\Util\PropertyPath;
  19. class DelegatingValidator implements FormValidatorInterface
  20. {
  21. private $validator;
  22. public function __construct(ValidatorInterface $validator)
  23. {
  24. $this->validator = $validator;
  25. }
  26. /**
  27. * Validates the form and its domain object.
  28. *
  29. * @param FormInterface $form A FormInterface instance
  30. */
  31. public function validate(FormInterface $form)
  32. {
  33. if ($form->isRoot()) {
  34. $mapping = array();
  35. $forms = array();
  36. $this->buildFormPathMapping($form, $mapping);
  37. $this->buildDataPathMapping($form, $mapping);
  38. $this->buildNamePathMapping($form, $forms);
  39. $this->resolveMappingPlaceholders($mapping, $forms);
  40. // Validate the form in group "Default"
  41. // Validation of the data in the custom group is done by validateData(),
  42. // which is constrained by the Execute constraint
  43. if ($form->hasAttribute('validation_constraint')) {
  44. $violations = $this->validator->validateValue(
  45. $form->getData(),
  46. $form->getAttribute('validation_constraint'),
  47. self::getFormValidationGroups($form)
  48. );
  49. if ($violations) {
  50. foreach ($violations as $violation) {
  51. $propertyPath = new PropertyPath($violation->getPropertyPath());
  52. $template = $violation->getMessageTemplate();
  53. $parameters = $violation->getMessageParameters();
  54. $error = new FormError($template, $parameters);
  55. $child = $form;
  56. foreach ($propertyPath->getElements() as $element) {
  57. $children = $child->getChildren();
  58. if (!isset($children[$element])) {
  59. $form->addError($error);
  60. break;
  61. }
  62. $child = $children[$element];
  63. }
  64. $child->addError($error);
  65. }
  66. }
  67. } elseif ($violations = $this->validator->validate($form)) {
  68. foreach ($violations as $violation) {
  69. $propertyPath = $violation->getPropertyPath();
  70. $template = $violation->getMessageTemplate();
  71. $parameters = $violation->getMessageParameters();
  72. $error = new FormError($template, $parameters);
  73. foreach ($mapping as $mappedPath => $child) {
  74. if (preg_match($mappedPath, $propertyPath)) {
  75. $child->addError($error);
  76. continue 2;
  77. }
  78. }
  79. $form->addError($error);
  80. }
  81. }
  82. }
  83. }
  84. /**
  85. * Validates the data of a form
  86. *
  87. * This method is called automatically during the validation process.
  88. *
  89. * @param FormInterface $form The validated form
  90. * @param ExecutionContext $context The current validation context
  91. */
  92. static public function validateFormData(FormInterface $form, ExecutionContext $context)
  93. {
  94. if (is_object($form->getData()) || is_array($form->getData())) {
  95. $propertyPath = $context->getPropertyPath();
  96. $graphWalker = $context->getGraphWalker();
  97. // The Execute constraint is called on class level, so we need to
  98. // set the property manually
  99. $context->setCurrentProperty('data');
  100. // Adjust the property path accordingly
  101. if (!empty($propertyPath)) {
  102. $propertyPath .= '.';
  103. }
  104. $propertyPath .= 'data';
  105. foreach (self::getFormValidationGroups($form) as $group) {
  106. $graphWalker->walkReference($form->getData(), $group, $propertyPath, true);
  107. }
  108. }
  109. }
  110. static protected function getFormValidationGroups(FormInterface $form)
  111. {
  112. $groups = null;
  113. if ($form->hasAttribute('validation_groups')) {
  114. $groups = $form->getAttribute('validation_groups');
  115. }
  116. $currentForm = $form;
  117. while (!$groups && $currentForm->hasParent()) {
  118. $currentForm = $currentForm->getParent();
  119. if ($currentForm->hasAttribute('validation_groups')) {
  120. $groups = $currentForm->getAttribute('validation_groups');
  121. }
  122. }
  123. if (null === $groups) {
  124. $groups = array('Default');
  125. }
  126. return (array) $groups;
  127. }
  128. private function buildFormPathMapping(FormInterface $form, array &$mapping, $formPath = 'children', $namePath = '')
  129. {
  130. foreach ($form->getAttribute('error_mapping') as $nestedDataPath => $nestedNamePath)
  131. {
  132. $mapping['/^'.preg_quote($formPath.'.data.'.$nestedDataPath).'(?!\w)/'] = $namePath.'.'.$nestedNamePath;
  133. }
  134. $iterator = new VirtualFormAwareIterator($form->getChildren());
  135. $iterator = new \RecursiveIteratorIterator($iterator);
  136. foreach ($iterator as $child) {
  137. $path = (string) $child->getAttribute('property_path');
  138. $parts = explode('.', $path, 2);
  139. $nestedNamePath = $namePath.'.'.$child->getName();
  140. if ($child->hasChildren() || isset($parts[1])) {
  141. $nestedFormPath = $formPath.'['.trim($parts[0], '[]').']';
  142. }
  143. else {
  144. $nestedFormPath = $formPath.'.data.'.$parts[0];
  145. }
  146. if (isset($parts[1])) {
  147. $nestedFormPath .= '.data.'.$parts[1];
  148. }
  149. if ($child->hasChildren()) {
  150. $this->buildFormPathMapping($child, $mapping, $nestedFormPath, $nestedNamePath);
  151. }
  152. $mapping['/^'.preg_quote($nestedFormPath, '/').'(?!\w)/'] = $child;
  153. }
  154. }
  155. private function buildDataPathMapping(FormInterface $form, array &$mapping, $dataPath = 'data', $namePath = '')
  156. {
  157. foreach ($form->getAttribute('error_mapping') as $nestedDataPath => $nestedNamePath)
  158. {
  159. $mapping['/^'.preg_quote($dataPath.'.'.$nestedDataPath).'(?!\w)/'] = $namePath.'.'.$nestedNamePath;
  160. }
  161. $iterator = new VirtualFormAwareIterator($form->getChildren());
  162. $iterator = new \RecursiveIteratorIterator($iterator);
  163. foreach ($iterator as $child) {
  164. $path = (string) $child->getAttribute('property_path');
  165. $nestedNamePath = $namePath.'.'.$child->getName();
  166. if (strpos($path, '[') === 0) {
  167. $nestedDataPaths = array($dataPath.$path);
  168. } else {
  169. $nestedDataPaths = array($dataPath.'.'.$path);
  170. if ($child->hasChildren()) {
  171. $nestedDataPaths[] = $dataPath.'['.$path.']';
  172. }
  173. }
  174. if ($child->hasChildren()) {
  175. // Needs when collection implements the Iterator
  176. // or for array used the Valid validator.
  177. if (is_array($child->getData()) || $child->getData() instanceof \Traversable) {
  178. $this->buildDataPathMapping($child, $mapping, $dataPath, $nestedNamePath);
  179. }
  180. foreach ($nestedDataPaths as $nestedDataPath) {
  181. $this->buildDataPathMapping($child, $mapping, $nestedDataPath, $nestedNamePath);
  182. }
  183. }
  184. foreach ($nestedDataPaths as $nestedDataPath) {
  185. $mapping['/^'.preg_quote($nestedDataPath, '/').'(?!\w)/'] = $child;
  186. }
  187. }
  188. }
  189. private function buildNamePathMapping(FormInterface $form, array &$forms, $namePath = '')
  190. {
  191. $iterator = new VirtualFormAwareIterator($form->getChildren());
  192. $iterator = new \RecursiveIteratorIterator($iterator);
  193. foreach ($iterator as $child) {
  194. $nestedNamePath = $namePath.'.'.$child->getName();
  195. $forms[$nestedNamePath] = $child;
  196. if ($child->hasChildren()) {
  197. $this->buildNamePathMapping($child, $forms, $nestedNamePath);
  198. }
  199. }
  200. }
  201. private function resolveMappingPlaceholders(array &$mapping, array $forms)
  202. {
  203. foreach ($mapping as $pattern => $form) {
  204. if (is_string($form)) {
  205. if (!isset($forms[$form])) {
  206. throw new FormException(sprintf('The child form with path "%s" does not exist', $form));
  207. }
  208. $mapping[$pattern] = $forms[$form];
  209. }
  210. }
  211. }
  212. }