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;
}
此选项仅对根表单有效,用于指定验证器将使用哪些组。
您好,我们必须添加一个 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);
}
}
}
我尝试使用一些简单的组规则来验证 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;
}
此选项仅对根表单有效,用于指定验证器将使用哪些组。
您好,我们必须添加一个 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);
}
}
}