Skip to content

Commit ce067a3

Browse files
committed
feature #19496 [DX][Form][Validator] Add ability check if cocrete constraint fails. (Koc)
This PR was merged into the 3.3-dev branch. Discussion ---------- [DX][Form][Validator] Add ability check if cocrete constraint fails. | Q | A | | --- | --- | | Branch? | master | | Bug fix? | no | | New feature? | yes | | BC breaks? | no | | Deprecations? | no | | Tests pass? | wait for travis | | Fixed tickets | #15154 | | License | MIT | | Doc PR | should open | Sometimes for big forms with multiple constraints we should handle some errors separately. ``` php // when using validator $constraintViolations = $validator->validate(...); if (count($constraintViolations->findByCodes(UniqueEntity::NOT_UNIQUE_ERROR))) { // display some message or send email or etc } // when using forms if (count($form->getErrors()->findByCodes(UniqueEntity::NOT_UNIQUE_ERROR))) { // display some message or send email or etc } ``` This PR add some useful methods to handle this. Before we should iterate all failed constraints using foreach. Feel free to suggest better names for new methods. Commits ------- 29a3a7e Add ability retrieve errors by their code.
2 parents b59660c + 29a3a7e commit ce067a3

File tree

4 files changed

+133
-2
lines changed

4 files changed

+133
-2
lines changed

src/Symfony/Component/Form/FormErrorIterator.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Form\Exception\InvalidArgumentException;
1515
use Symfony\Component\Form\Exception\OutOfBoundsException;
1616
use Symfony\Component\Form\Exception\BadMethodCallException;
17+
use Symfony\Component\Validator\ConstraintViolation;
1718

1819
/**
1920
* Iterates over the errors of a form.
@@ -265,6 +266,27 @@ public function seek($position)
265266
}
266267
}
267268

269+
/**
270+
* Creates iterator for errors with specific codes.
271+
*
272+
* @param string|string[] $codes The codes to find
273+
*
274+
* @return static New instance which contains only specific errors.
275+
*/
276+
public function findByCodes($codes)
277+
{
278+
$codes = (array) $codes;
279+
$errors = array();
280+
foreach ($this as $error) {
281+
$cause = $error->getCause();
282+
if ($cause instanceof ConstraintViolation && in_array($cause->getCode(), $codes, true)) {
283+
$errors[] = $error;
284+
}
285+
}
286+
287+
return new static($this->form, $errors);
288+
}
289+
268290
/**
269291
* Utility function for indenting multi-line strings.
270292
*
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Tests;
13+
14+
use Symfony\Component\EventDispatcher\EventDispatcher;
15+
use Symfony\Component\Form\FormBuilder;
16+
use Symfony\Component\Form\FormError;
17+
use Symfony\Component\Form\FormErrorIterator;
18+
use Symfony\Component\Validator\ConstraintViolation;
19+
20+
class FormErrorIteratorTest extends \PHPUnit_Framework_TestCase
21+
{
22+
/**
23+
* @dataProvider findByCodesProvider
24+
*/
25+
public function testFindByCodes($code, $violationsCount)
26+
{
27+
if (!class_exists(ConstraintViolation::class)) {
28+
$this->markTestSkipped('Validator component required.');
29+
}
30+
31+
$formBuilder = new FormBuilder(
32+
'form',
33+
null,
34+
new EventDispatcher(),
35+
$this->getMock('Symfony\Component\Form\FormFactoryInterface'),
36+
array()
37+
);
38+
39+
$form = $formBuilder->getForm();
40+
41+
$cause = new ConstraintViolation('Error 1!', null, array(), null, '', null, null, 'code1');
42+
$form->addError(new FormError('Error 1!', null, array(), null, $cause));
43+
$cause = new ConstraintViolation('Error 2!', null, array(), null, '', null, null, 'code1');
44+
$form->addError(new FormError('Error 2!', null, array(), null, $cause));
45+
$cause = new ConstraintViolation('Error 3!', null, array(), null, '', null, null, 'code2');
46+
$form->addError(new FormError('Error 3!', null, array(), null, $cause));
47+
$formErrors = $form->getErrors();
48+
49+
$specificFormErrors = $formErrors->findByCodes($code);
50+
$this->assertInstanceOf(FormErrorIterator::class, $specificFormErrors);
51+
$this->assertCount($violationsCount, $specificFormErrors);
52+
}
53+
54+
public function findByCodesProvider()
55+
{
56+
return array(
57+
array('code1', 2),
58+
array(array('code1', 'code2'), 3),
59+
array('code3', 0),
60+
);
61+
}
62+
}

src/Symfony/Component/Validator/ConstraintViolationList.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,24 @@ public function offsetUnset($offset)
158158
{
159159
$this->remove($offset);
160160
}
161+
162+
/**
163+
* Creates iterator for errors with specific codes.
164+
*
165+
* @param string|string[] $codes The codes to find
166+
*
167+
* @return static New instance which contains only specific errors.
168+
*/
169+
public function findByCodes($codes)
170+
{
171+
$codes = (array) $codes;
172+
$violations = array();
173+
foreach ($this as $violation) {
174+
if (in_array($violation->getCode(), $codes, true)) {
175+
$violations[] = $violation;
176+
}
177+
}
178+
179+
return new static($violations);
180+
}
161181
}

src/Symfony/Component/Validator/Tests/ConstraintViolationListTest.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,35 @@ public function testToString()
128128
$this->assertEquals($expected, (string) $this->list);
129129
}
130130

131-
protected function getViolation($message, $root = null, $propertyPath = null)
131+
/**
132+
* @dataProvider findByCodesProvider
133+
*/
134+
public function testFindByCodes($code, $violationsCount)
132135
{
133-
return new ConstraintViolation($message, $message, array(), $root, $propertyPath, null);
136+
$violations = array(
137+
$this->getViolation('Error', null, null, 'code1'),
138+
$this->getViolation('Error', null, null, 'code1'),
139+
$this->getViolation('Error', null, null, 'code2'),
140+
);
141+
$list = new ConstraintViolationList($violations);
142+
143+
$specificErrors = $list->findByCodes($code);
144+
145+
$this->assertInstanceOf(ConstraintViolationList::class, $specificErrors);
146+
$this->assertCount($violationsCount, $specificErrors);
147+
}
148+
149+
public function findByCodesProvider()
150+
{
151+
return array(
152+
array('code1', 2),
153+
array(array('code1', 'code2'), 3),
154+
array('code3', 0),
155+
);
156+
}
157+
158+
protected function getViolation($message, $root = null, $propertyPath = null, $code = null)
159+
{
160+
return new ConstraintViolation($message, $message, array(), $root, $propertyPath, null, null, $code);
134161
}
135162
}

0 commit comments

Comments
 (0)