Symfony2 – validation不适用于embedded式表单types

我有一个组合两个实体(用户和configuration文件)的表单。

validation似乎在来自用户实体的表单的第一部分上工作,并且是表单的基础。

ProfileType包含在UserType中。 表单呈现正确,并显示正确的信息,所以它似乎正确连接到configuration文件实体。 这只是在ProfileType上打破的validation。

任何想法,为什么一个部分将validation,而另一个不会?

代码如下:

Validation.yml

DEMO\DemoBundle\Entity\User\Profile: properties: address1: - NotBlank: { groups: [profile] } name: - NotBlank: { groups: [profile] } companyName: - NotBlank: { groups: [profile] } DEMO\DemoBundle\Entity\User\User: properties: username: - NotBlank: groups: profile message: Username cannot be left blank. email: - NotBlank: groups: profile message: Email cannot be left blank - Email: groups: profile message: The email "{{ value }}" is not a valid email. checkMX: true password: - MaxLength: { limit: 20, message: "Your password must not exceed {{ limit }} characters." } - MinLength: { limit: 4, message: "Your password must have at least {{ limit }} characters." } - NotBlank: ~ 

UserType.php

 namespace DEMO\DemoBundle\Form\Type\User; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackValidator; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormError; use DEMO\DemoBundle\Form\Type\User\ProfileType; class UserType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder->add('username'); $builder->add('email'); $builder->add('profile', new ProfileType()); } public function getDefaultOptions(array $options) { return array( 'data_class' => 'DEMO\DemoBundle\Entity\User\User', 'validation_groups' => array('profile') ); } public function getName() { return 'user'; } } 

ProfileType.php

 namespace DEMO\DemoBundle\Form\Type\User; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackValidator; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormError; class ProfileType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder->add('name'); $builder->add('companyName', null, array('label' => 'Company Name')); $builder->add('address1', null, array('label' => 'Address 1')); $builder->add('address2', null, array('label' => 'Address 2')); $builder->add('city'); $builder->add('county'); $builder->add('postcode'); $builder->add('telephone'); } public function getDefaultOptions(array $options) { return array( 'data_class' => 'DEMO\DemoBundle\Entity\User\Profile', ); } public function getName() { return 'profile'; } } 

调节器

 $user = $this->get('security.context')->getToken()->getUser(); $form = $this->createForm(new UserType(), $user); if ($request->getMethod() == 'POST') { $form->bindRequest($request); if ($form->isValid()) { // Get $_POST data and submit to DB $em = $this->getDoctrine()->getEntityManager(); $em->persist($user); $em->flush(); // Set "success" flash notification $this->get('session')->setFlash('success', 'Profile saved.'); } } return $this->render('DEMODemoBundle:User\Dashboard:profile.html.twig', array('form' => $form->createView())); 

我花了一个年龄search,发现它是在我的父types的类中的setDefaults()数组中添加了'cascade_validation' => true (已经在线程中提到过)。 这会导致实体约束validation在表单中显示的子types中触发。 例如

 public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( ... 'cascade_validation' => true, )); } 

对于集合,还要确保将'cascade_validation' => true添加到窗体集合字段的$options数组中。 例如

 $builder->add('children', 'collection', array( 'type' => new ChildType(), 'cascade_validation' => true, )); 

这将会在集合中使用的子实体中进行UniqueEntityvalidation。

对那些使用Symfony 3.0及更高版本的人来说: cascade_validation选项已被删除 。 相反,使用以下embedded式表单:

 $builder->add('embedded_data', CustomFormType::class, array( 'constraints' => array(new Valid()), )); 

对不起,加了一个稍微离题的答案(Symfony 3 vs. 2),这个老的线程,但在这里find这个信息将节省我几个小时今天。

根据表单types文档,您也可以使用Valid约束而不是cascade_validation选项。

 $builder->add('children', 'collection', array( 'type' => new ChildType(), 'constraints' => array(new Valid()), )); 

所有者实体示例:

 /** * @var Collection * * @ORM\OneToMany(targetEntity="Child", ...) * @Assert\Valid() */ private $children 

属于Symfony 2.3

使用embedded式表单和validation组可能会非常痛苦:Annotation @Assert \ Valid()不会为我工作(没有组合就没关系)。 在DefaultOptions上插入'cascade_validation'=> true是关键。 你不需要在 – > add()上重复这个。 请注意:HTML 5validation不能与validation组一起使用。

例:

2个地址的集合。 两者都是1:1单向。 每个都有一个不同的(!)validation组。

  class TestCollection{ //(...) /** * @var string * @Assert\NotBlank(groups={"parentValGroup"}) * @ORM\Column(name="name", type="string", length=255, nullable=true) */ protected $name; /** * @var \Demo\Bundle\Entity\TestAddress * @Assert\Type(type="Demo\Bundle\Entity\TestAddress") * @ORM\OneToOne(targetEntity="TestAddress",cascade={"persist","remove"},orphanRemoval=true) * @ORM\JoinColumn(name="billing_address__id", referencedColumnName="id") */ protected $billingAddress; /** * @var \Demo\Bundle\Entity\TestAddress * @Assert\Type(type="Demo\Bundle\Entity\TestAddress") * @ORM\OneToOne(targetEntity="TestAddress",cascade={"persist","remove"}, orphanRemoval=true) * @ORM\JoinColumn(name="shipping_address__id", referencedColumnName="id") */ protected $shippingAddress; //(...) } 

地址实体

 class TestAddress { /** * @var string * @Assert\NotBlank(groups={"firstname"}) * @ORM\Column(name="firstname", type="string", length=255, nullable=true) */ private $firstname; /** * @var string * @Assert\NotBlank(groups={"lastname"}) * @ORM\Column(name="lastname", type="string", length=255, nullable=true) */ private $lastname; /** * @var string * @Assert\Email(groups={"firstname","lastname"}) * @ORM\Column(name="email", type="string", length=255, nullable=true) */ private $email; 

地址types – 更改validation组的能力

 class TestAddressType extends AbstractType { protected $validation_group=['lastname'];//switch group public function __construct($validation_group=null) { if($validation_group!=null) $this->validation_group=$validation_group; } public function buildForm(FormBuilderInterface $builder, array $options) { //disable html5 validation: it suchs with groups $builder ->add('firstname',null,array('required'=>false)) ->add('lastname',null,array('required'=>false)) ->add('email',null,array('required'=>false)) ; } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'Demo\Bundle\Entity\TestAddress', 'validation_groups' => $this->validation_group, )); } (...) 

最后是CollectionType

 class TestCollectionType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name') ->add('billingAddress', new TestAddressType(['lastname','firstname'])) ->add('shippingAddress', new TestAddressType(['firstname'])) ; } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'Demo\Bundle\Entity\TestCollection', 'validation_groups' => array('parentValGroup'), 'cascade_validation' => true )); } //(...) 

希望能帮助到你..

您还必须在您的ProfiletType添加validation_groups 。 如果存在,则根据其data_class分别在每个表单types中进行validation。

你使用YML还是注解?

我尝试在我的父窗体类上应用cascade_validation选项,但validation仍然没有发生。 在阅读了一些文档之后,我去了app/config/config.yml ,发现framework->validation下的enable_annotations被设置为true 。 显然,如果这是真的,validation服务不孤单读任何validation.yml文件。 所以我只是把它改成了false ,现在表单正在validation正常。

我正在寻找完全相同的东西,这是我发现的

http://symfony.com/doc/master/book/forms.html#forms-embedding-single-object

你需要告诉主要实体validation它的子实体,如下所示:

 /** * @Assert\Type(type="AppBundle\Entity\Category") * @Assert\Valid() */ private $subentity; 

我已经testing了这个symfony 2.8,它的工作原理。

从我的控制器:

 $form = $this->get('form.factory') ->createNamedBuilder('form_data', 'form', $item, array('cascade_validation' => true)) ->add('data', new ItemDataType()) ->add('assets', new ItemAssetsType($this->locale)) ->add('contact', new ItemContactType()) ->add('save', 'submit', array( 'label' => 'Save', 'attr' => array('class' => 'btn') ) ) ->getForm(); 

第四个参数在:: createNamedBuilder – array('cascade_validation' => true))