Symfony 4 CollectionType validation groups not working

symfony constraints
symfony assertexpression

I try to validate FormType inside CollectionType with some simple groups rules but It doesn't work, but if i try to make the same without validations groups, it's work fine. Any idea? This is a complete and simple exemple that reproduct the error https://github.com/ychakroun/symfony-collection-type-issue

    /**
    * Sticker
    *
    * @ORM\Table(name="sticker")
    * @ORM\Entity(repositoryClass="App\Repository\StickerRepository")
    */
    class Sticker 
    {
       /**
         * @var \Doctrine\Common\Collections\Collection
         *
         * @ORM\OneToMany(targetEntity="App\Entity\Link", mappedBy="sticker", cascade={"persist", "remove"}, orphanRemoval=true)
         * @ORM\OrderBy({"position"="ASC"})
         * @Assert\Valid()
         */
        private $links;
    }
/**
 * Link
 *
 * @ORM\Table(name="link")
 * @ORM\Entity(repositoryClass="App\Repository\LinkRepository")
 */
    class Link
    {

    /**
     * @var mixed
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string|null
     * @Assert\NotBlank()
     *
     * @ORM\Column(name="title", type="string")
     */
    private $title;

    /**
     * @var bool
     *
     * @ORM\Column(name="external", type="boolean")
     */
    private $external;

    /**
     *
     * @var string|null
     *
     * @Assert\NotBlank(groups={"isExternal"})
     * @Assert\Url(groups={"isExternal"})
     * @ORM\Column(name="url", type="text", nullable=true)
     */
    private $url;

    /**
     * @var \App\Entity\PageVersion|null
     *
     * @Assert\NotBlank(groups={"isInternal"})
     * @ORM\ManyToOne(targetEntity="App\Entity\PageVersion")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="page_version_id", referencedColumnName="id", nullable=true)
     * })
     */
    private $pageVersion;

    /**
     * @var \App\Entity\Category|null
     *
     * @Assert\NotBlank(groups={"isInternal"})
     * @ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="links")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=true)
     * })
     */
    private $category;

    /**
     * @var \App\Entity\Sticker|null
     *
     * @ORM\ManyToOne(targetEntity="App\Entity\Sticker", inversedBy="links")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="sticker_id", referencedColumnName="id", nullable=true)
     * })
     */
    private $sticker;
}

And this is the forms i use:

class StickerType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('links', CollectionType::class, [
                'entry_type' => LinkType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'delete_empty' => true,
                'attr' => [
                    'class' => 'collection',
                ],
                'by_reference' => false,
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Sticker::class,
        ]);
    }
}
class LinkType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title', TextType::class, [
                'label' => 'Titre du menu:',
                'attr' => [
                    'input-group' => 'true',
                ],
            ])
            ->add('external', ChoiceType::class, [
                'label' => false,
                'expanded' => true,
                'choices' => [
                    'Lien interne' => false,
                    'Lien externe' => true,
                ],
                'choice_attr' => [
                    'class' => 'link-type',
                ],
                'label_attr' => [
                    'class' => 'btn-group btn-group-toggle',
                    'data-toggle' => 'buttons',
                ]
            ])
            ->add('url', UrlType::class, [
                'label' => 'SAISISSEZ L\'URL EXTERNE',
                'attr' => ['placeholder' => 'https://'],
            ])
            ->add('pageVersion', EntityType::class, [
                'required' => false,
                'class' => Page::class,
                'choice_label' => 'name',
            ])
            ->add('category', EntityType::class, [
                'required' => false,
                'class' => Category::class,
                'choice_label' => 'title',
                'query_builder' => function (CategoryRepository $er) {
                    return $er->createQueryBuilder('c')->where('c.enabled = 1');
                },
            ])
            ->add('position', HiddenType::class, [
                'attr' => [
                    'class' => 'my-position',
                ],
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Link::class,
            'cascade_validation' => true,
            'validation_groups' => function (FormInterface $form) {
                /** @var Link $link */
                $link = $form->getData();
                $groups = ['Default'];
                if ($link->getExternal()) {
                    $groups[] = 'isExternal';
                } else {
                    $groups[] = 'isInternal';
                }

                return $groups;
            },
        ]);
    }
}

We can see that the url field is validated and it's blank

If i try to remove groups={"isExternal"} from link entity, the validation will work, like in this image:


I think you need to add the validation groups on the Sticker entity too :

/**
 * Sticker
 *
 * @ORM\Table(name="sticker")
 * @ORM\Entity(repositoryClass="App\Repository\StickerRepository")
 */
class Sticker 
{
    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\OneToMany(targetEntity="App\Entity\Link", mappedBy="sticker", cascade={"persist", "remove"}, orphanRemoval=true)
     * @ORM\OrderBy({"position"="ASC"})
     * @Assert\Valid(groups={"isInternal", "isExternal"})
     */
    private $links;
}

CollectionType Validation Group � Issue #31441 � symfony/symfony , The first page is with Collection Type, and it's not working, The second is the same, but without CollectionType, and it's work. Thanks you for your� Symfony version(s) affected: 4.2.x. Description I try to add some validation groups test in Form CollectionType instance but this doesn't work, but when i remove the groups, the validation work very fine. I already test the same think in 3.4.7 and it's work with and without groups validation. How to reproduce


This option is only valid on the root form and is used to specify which groups will be used by the validator.

This is the response https://github.com/symfony/symfony/issues/31441

Validation groups are not applied on embedded FormType � Issue , GitHub is home to over 50 million developers working together to host and . com/blog/new-in-symfony-3-4-groups-support-for-the-valid-constraint a CollectionType and using validation_groups within the embedded form� If no groups are specified, all constraints that belong to the group Default will be applied. In a full stack Symfony project, you’ll usually work with validation indirectly through the form library. For information on how to use validation groups inside forms, see How to Define the Validation Groups to Use.


Hello we must add an addEventListener

class StickerType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('links', CollectionType::class, [
                'entry_type' => LinkType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'delete_empty' => true,
                'attr' => [
                    'class' => 'collection',
                ],
                'by_reference' => false,
            ])
            ->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmit'));
        ;
        ;
    }
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Sticker::class,
        ]);
    }

    public function onPreSubmit(FormEvent $event)
    {


        if ($event->getData()) {

            $data = $event->getData();
            $data['links'] = array_values($data['links']);
            $event->setData($data);
        }
    }
}

Collection (Symfony Docs), allowExtraFields; allowMissingFields; extraFieldsMessage; fields; groups For example, you might validate the email key using the Email constraint and the inventory key sure that certain collection keys are present and that extra keys are not present. This work, including the code samples, is licensed under a Creative� CollectionType Field: This field type is used to render a “collection” of some field or form. In the easiest sense, it could be an array of TextType fields that populate an array emails values.


Validation Groups, How to Apply only a Subset of all Your Validation Constraints (Validation Groups) : By when validating an object all constraints of this class will be checked whether or not In a full stack Symfony project, you'll usually work with validation indirectly For information on how to use validation groups inside forms, see How to� Validation¶. Validation is a very common task in web applications. Data entered in forms needs to be validated. Data also needs to be validated before it is written into a database or passed to a web service.


CollectionType Field (Symfony Docs), CollectionType Field: This field type is used to render a “collection” of some field or and inherited by this form type is available running this command in your app: For example, suppose you have an emails field that corresponds to an array of If set to true , then if an existing item is not contained in the submitted data,� And by default, Symfony reads all of the validation annotations from the top-level class only. When it sees an embedded object, or an array of embedded objects, like the genusScientists property, it does not go deeper and read the annotations from the GenusScientist class. In other words, Symfony only validates the top-level object. Double-lame!


Validation Constraints Reference (Symfony Docs), Validation Constraints Reference: The Validator is designed to validate objects against constraints. In real life, a constraint could be: “The cake must not be� @guillaumesmo It seems like Assert\Valid() is not cascading the validation group. If you add the validation_groups setting you have in CategoryType also into TaskType it works as expected. I guess I had the same issue once and I was just using cascade_validation instead of Assert\Valid. Actually therefore I have never used Assert\Valid().