Skip to content

Commit dcf6ad3

Browse files
committed
Merge branch '5.4' into 6.0
* 5.4: Make FormErrorIterator generic [symfony/mailjet-mailer] Fix invalid mailjet error managment typehint of DkimOptions algorithm wrong Remove extra space in NotificationEmail Fix the usage of the Valid constraints in array-based forms Fix return value of `NullToken::getUser()` [DI] fix `ServiceSubscriberTrait` bug where parent has `__call()` [HttpClient] Fix reading proxy settings from dotenv when curl is used [Process] Don't return executable directories in PhpExecutableFinder Center icons vertically in trace list
2 parents 2465793 + 7526793 commit dcf6ad3

File tree

3 files changed

+50
-7
lines changed

3 files changed

+50
-7
lines changed

Extension/Validator/Constraints/FormValidator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public function validate(mixed $form, Constraint $formConstraint)
113113
foreach ($constraints as $constraint) {
114114
// For the "Valid" constraint, validate the data in all groups
115115
if ($constraint instanceof Valid) {
116-
if (\is_object($data)) {
116+
if (\is_object($data) || \is_array($data)) {
117117
$validator->atPath('data')->validate($data, $constraint, $groups);
118118
}
119119

FormErrorIterator.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@
2929
*
3030
* @author Bernhard Schussek <[email protected]>
3131
*
32-
* @implements \ArrayAccess<int, FormError|FormErrorIterator>
33-
* @implements \RecursiveIterator<int, FormError|FormErrorIterator>
34-
* @implements \SeekableIterator<int, FormError|FormErrorIterator>
32+
* @template T of FormError|FormErrorIterator
33+
*
34+
* @implements \ArrayAccess<int, T>
35+
* @implements \RecursiveIterator<int, T>
36+
* @implements \SeekableIterator<int, T>
3537
*/
3638
class FormErrorIterator implements \RecursiveIterator, \SeekableIterator, \ArrayAccess, \Countable
3739
{
@@ -41,10 +43,14 @@ class FormErrorIterator implements \RecursiveIterator, \SeekableIterator, \Array
4143
public const INDENTATION = ' ';
4244

4345
private $form;
46+
47+
/**
48+
* @var list<T>
49+
*/
4450
private array $errors;
4551

4652
/**
47-
* @param list<FormError|self> $errors
53+
* @param list<T> $errors
4854
*
4955
* @throws InvalidArgumentException If the errors are invalid
5056
*/
@@ -72,7 +78,7 @@ public function __toString(): string
7278
$string .= 'ERROR: '.$error->getMessage()."\n";
7379
} else {
7480
/* @var self $error */
75-
$string .= $error->form->getName().":\n";
81+
$string .= $error->getForm()->getName().":\n";
7682
$string .= self::indent((string) $error);
7783
}
7884
}
@@ -90,6 +96,8 @@ public function getForm(): FormInterface
9096

9197
/**
9298
* Returns the current element of the iterator.
99+
*
100+
* @return T An error or an iterator containing nested errors
93101
*/
94102
public function current(): FormError|self
95103
{
@@ -146,6 +154,8 @@ public function offsetExists(mixed $position): bool
146154
*
147155
* @param int $position The position
148156
*
157+
* @return T
158+
*
149159
* @throws OutOfBoundsException If the given position does not exist
150160
*/
151161
public function offsetGet(mixed $position): FormError|self
@@ -192,7 +202,10 @@ public function getChildren(): self
192202
throw new LogicException(sprintf('The current element is not iterable. Use "%s" to get the current element.', self::class.'::current()'));
193203
}
194204

195-
return current($this->errors);
205+
/** @var self $children */
206+
$children = current($this->errors);
207+
208+
return $children;
196209
}
197210

198211
/**

Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Form\AbstractType;
1616
use Symfony\Component\Form\CallbackTransformer;
1717
use Symfony\Component\Form\Exception\TransformationFailedException;
18+
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
1819
use Symfony\Component\Form\Extension\Core\Type\DateType;
1920
use Symfony\Component\Form\Extension\Core\Type\FormType;
2021
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
@@ -321,6 +322,35 @@ public function testCascadeValidationToChildFormsWithTwoValidConstraints2()
321322
$this->assertSame('children[author].data.email', $violations[1]->getPropertyPath());
322323
}
323324

325+
public function testCascadeValidationToArrayChildForm()
326+
{
327+
$form = $this->formFactory->create(FormType::class, null, [
328+
'data_class' => Review::class,
329+
])
330+
->add('title')
331+
->add('customers', CollectionType::class, [
332+
'mapped' => false,
333+
'entry_type' => CustomerType::class,
334+
'allow_add' => true,
335+
'constraints' => [new Valid()],
336+
]);
337+
338+
$form->submit([
339+
'title' => 'Sample Title',
340+
'customers' => [
341+
['email' => null],
342+
],
343+
]);
344+
345+
$violations = $this->validator->validate($form);
346+
347+
$this->assertCount(2, $violations);
348+
$this->assertSame('This value should not be blank.', $violations[0]->getMessage());
349+
$this->assertSame('data.rating', $violations[0]->getPropertyPath());
350+
$this->assertSame('This value should not be blank.', $violations[1]->getMessage());
351+
$this->assertSame('children[customers].data[0].email', $violations[1]->getPropertyPath());
352+
}
353+
324354
public function testCascadeValidationToChildFormsUsingPropertyPathsValidatedInSequence()
325355
{
326356
$form = $this->formFactory->create(FormType::class, null, [

0 commit comments

Comments
 (0)