Correctly map the errors of newly created elements in CollectionTypes

Before there were just shown on the parent, now they get mapped to the right field
This commit is contained in:
Jan Böhmer 2023-03-05 23:05:58 +01:00
parent 86f77fde1a
commit 1446aab451

View file

@ -43,13 +43,16 @@ namespace App\Form;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use http\Cookie;
use ReflectionClass; use ReflectionClass;
use ReflectionException; use ReflectionException;
use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Event\PreSubmitEvent;
use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormConfigBuilder; use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormConfigInterface;
use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface; 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. * 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 * This prevents issues when the collection that is edited uses a OrderBy annotation and therefore the direction of the
* elements can change during requests. * 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 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 }, 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.... * 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 //We have to use FormConfigBuilder::class here, because options is private and not available in sub classes
$reflection = new ReflectionClass(FormConfigBuilder::class); $reflection = new ReflectionClass(FormConfigBuilder::class);
$property = $reflection->getProperty('options'); $property = $reflection->getProperty('options');