Symfony 4 CollectionType 验证组不工作

Symfony 4 CollectionType validation groups not working

我尝试使用一些简单的组规则来验证 CollectionType 中的 FormType,但它不起作用,但如果我尝试在没有验证组的情况下进行同样的操作,它就可以正常工作。 任何的想法? 这是一个完整而简单的示例,它重现了错误 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;
}

这是我使用的表格:

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;
            },
        ]);
    }
}

我们可以看到 url 字段已通过验证并且为空

如果我尝试从 link 实体中删除 groups={"isExternal"},验证将起作用,如下图所示:

我认为您也需要在 Sticker 实体上添加验证组:

/**
 * 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;
}

此选项仅对根表单有效,用于指定验证器将使用哪些组。

这是回复https://github.com/symfony/symfony/issues/31441

您好,我们必须添加一个 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);
        }
    }
}