4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Form\Extension\Validator\ViolationMapper
;
14 use Symfony\Component\Form\Exception\OutOfBoundsException
;
15 use Symfony\Component\PropertyAccess\PropertyPath
;
16 use Symfony\Component\PropertyAccess\PropertyPathInterface
;
19 * @author Bernhard Schussek <bschussek@gmail.com>
21 class ViolationPath
implements \IteratorAggregate
, PropertyPathInterface
26 private $elements = array();
31 private $isIndex = array();
36 private $mapsForm = array();
41 private $pathAsString = '';
49 * Creates a new violation path from a string.
51 * @param string $violationPath The property path of a {@link ConstraintViolation}
54 public function __construct($violationPath)
56 $path = new PropertyPath($violationPath);
57 $elements = $path->getElements();
60 for ($i = 0, $l = count($elements); $i < $l; ++
$i) {
62 // The element "data" has not yet been passed
63 if ('children' === $elements[$i] && $path->isProperty($i)) {
64 // Skip element "children"
67 // Next element must exist and must be an index
68 // Otherwise consider this the end of the path
69 if ($i >= $l || !$path->isIndex($i)) {
73 $this->elements
[] = $elements[$i];
74 $this->isIndex
[] = true;
75 $this->mapsForm
[] = true;
76 } elseif ('data' === $elements[$i] && $path->isProperty($i)) {
77 // Skip element "data"
85 $this->elements
[] = $elements[$i];
86 $this->isIndex
[] = $path->isIndex($i);
87 $this->mapsForm
[] = false;
90 // Neither "children" nor "data" property found
91 // Consider this the end of the path
95 // Already after the "data" element
96 // Pick everything as is
97 $this->elements
[] = $elements[$i];
98 $this->isIndex
[] = $path->isIndex($i);
99 $this->mapsForm
[] = false;
103 $this->length
= count($this->elements
);
105 $this->buildString();
111 public function __toString()
113 return $this->pathAsString
;
119 public function getLength()
121 return $this->length
;
127 public function getParent()
129 if ($this->length
<= 1) {
133 $parent = clone $this;
136 array_pop($parent->elements
);
137 array_pop($parent->isIndex
);
138 array_pop($parent->mapsForm
);
140 $parent->buildString();
148 public function getElements()
150 return $this->elements
;
156 public function getElement($index)
158 if (!isset($this->elements
[$index])) {
159 throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
162 return $this->elements
[$index];
168 public function isProperty($index)
170 if (!isset($this->isIndex
[$index])) {
171 throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
174 return !$this->isIndex
[$index];
180 public function isIndex($index)
182 if (!isset($this->isIndex
[$index])) {
183 throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
186 return $this->isIndex
[$index];
190 * Returns whether an element maps directly to a form.
192 * Consider the following violation path:
195 * children[address].children[office].data.street
198 * In this example, "address" and "office" map to forms, while
201 * @param integer $index The element index.
203 * @return Boolean Whether the element maps to a form.
205 * @throws OutOfBoundsException If the offset is invalid.
207 public function mapsForm($index)
209 if (!isset($this->mapsForm
[$index])) {
210 throw new OutOfBoundsException(sprintf('The index %s is not within the violation path', $index));
213 return $this->mapsForm
[$index];
217 * Returns a new iterator for this path
219 * @return ViolationPathIterator
221 public function getIterator()
223 return new ViolationPathIterator($this);
227 * Builds the string representation from the elements.
229 private function buildString()
231 $this->pathAsString
= '';
234 foreach ($this->elements
as $index => $element) {
235 if ($this->mapsForm
[$index]) {
236 $this->pathAsString
.= ".children[$element]";
238 $this->pathAsString
.= '.data'.($this->isIndex
[$index] ? "[$element]" : ".$element");
241 $this->pathAsString .= $this->isIndex[$index] ? "[$element]" : ".$element";
245 if ('' !== $this->pathAsString
) {
246 // remove leading dot
247 $this->pathAsString
= substr($this->pathAsString
, 1);