diff --git a/src/Form/CollectionTypeExtension.php b/src/Form/CollectionTypeExtension.php index 7ac5e56f..f36c7f96 100644 --- a/src/Form/CollectionTypeExtension.php +++ b/src/Form/CollectionTypeExtension.php @@ -43,13 +43,16 @@ namespace App\Form; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; +use http\Cookie; use ReflectionClass; use ReflectionException; use Symfony\Component\Form\AbstractTypeExtension; +use Symfony\Component\Form\Event\PreSubmitEvent; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\FormConfigInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; @@ -62,7 +65,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; * Perform a reindexing on CollectionType elements, by assigning the database id as index. * This prevents issues when the collection that is edited uses a OrderBy annotation and therefore the direction of the * elements can change during requests. - * Must me enabled by setting reindex_enable to true in Type options. + * Must be enabled by setting reindex_enable to true in Type options. */ class CollectionTypeExtension extends AbstractTypeExtension { @@ -135,6 +138,32 @@ class CollectionTypeExtension extends AbstractTypeExtension } } }, 100); //We need to have a higher priority then the PRE_SET_DATA listener on CollectionType + + // This event listener fixes the error mapping for newly created elements of collection types + // Without this method, the errors for newly created elements are shown on the parent element, as forms + // can not map it to the correct element. + $builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event) use ($options) { + $data = $event->getData(); + $form = $event->getForm(); + $config = $form->getConfig(); + + if (!is_array($data) && !$data instanceof Collection) { + return; + } + + if ($data instanceof Collection) { + $data = $data->toArray(); + } + + //The validator uses the number of the element as index, so we have to map the errors to the correct index + $error_mapping = []; + $n = 0; + foreach ($data as $key => $item) { + $error_mapping['['.$n.']'] = $key; + $n++; + } + $this->setOption($config, 'error_mapping', $error_mapping); + }); } /** @@ -142,8 +171,12 @@ class CollectionTypeExtension extends AbstractTypeExtension * This a bit hacky cause we access private properties.... * */ - public function setOption(FormBuilder $builder, string $option, $value): void + public function setOption(FormConfigInterface $builder, string $option, $value): void { + if (!$builder instanceof FormConfigBuilder) { + throw new \RuntimeException('This method only works with FormConfigBuilder instances.'); + } + //We have to use FormConfigBuilder::class here, because options is private and not available in sub classes $reflection = new ReflectionClass(FormConfigBuilder::class); $property = $reflection->getProperty('options');