PageRenderTime 41ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

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

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