Symfony2 -对嵌入表单类型无效的验证

时间:2022-10-22 18:17:32

I have a form that combines two entities (User and Profile).

我有一个合并了两个实体(用户和概要文件)的表单。

Validation seems to work on the first part of the form that comes form the User Entity and is the basis of the form.

验证似乎在表单的第一部分中起作用,它来自于用户实体,并且是表单的基础。

The ProfileType is included inside the UserType. The form renders correctly and displays the correct information, so it seems it is properly connected to the Profile entity. It's just the validation that is broken on the ProfileType.

ProfileType包含在UserType中。表单呈现正确并显示正确的信息,因此它似乎正确地连接到概要实体。只是ProfileType上的验证被破坏了。

Any idea as to why one part would validate and the other wouldn't?

你知道为什么一个部分会生效,另一个不会吗?

Code below:

下面的代码:

Validation.yml

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

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

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';
    }
}

Controller

控制器

$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()));

8 个解决方案

#1


111  

I spent an age searching and found that it was adding 'cascade_validation' => true to the setDefaults() array in my parent type's class that fixed it (as mentioned already in the thread). This causes the entity constraint validation to trigger in the child types shown in the form. e.g.

我花了很长时间搜索,发现它添加了'cascade_validation' => true给我父类型的类中的setDefaults()数组来修复它(正如在线程中已经提到的)。这导致实体约束验证在表单中显示的子类型中触发。如。

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

For collections, also make sure to add 'cascade_validation' => true to the $options array for the collection field on the form. e.g.

对于集合,还要确保在表单上的集合字段的$options数组中添加'cascade_validation' =>。如。

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

This will have the UniqueEntity validation take place as it should in the child entity used in the collection.

这将使UniqueEntity验证发生在集合中使用的子实体中。

#2


52  

A note to those using Symfony 3.0 and up: the cascade_validation option has been removed. Instead, use the following for embedded forms:

请注意使用Symfony 3.0和以上版本的用户:cascade_validation选项已经被删除。相反,在嵌入式表单中使用以下内容:

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

Sorry for adding to this old thread with a slightly off-topic answer (Symfony 3 vs. 2), but finding this information here would have saved me a few hours today.

很抱歉在这个旧的线程中添加了一个稍微偏离主题的答案(Symfony 3 vs. 2),但是在这里找到这些信息将会为我今天节省几个小时。

#3


33  

According to form type documentation you can also use Valid constraint instead of cascade_validation option.

根据表单类型文档,您还可以使用有效约束而不是cascade_validation选项。

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

Example from the owner entity:

业主实体的例子:

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

#4


4  

Belongs to Symfony 2.3

属于Symfony 2.3

Working with embedded forms and validation groups could be quite painful: Annotation @Assert\Valid() doesen't work for me (without groups it is ok). Insert 'cascade_validation' => true on the DefaultOptions is the key. You do not need to repeat this on the ->add(). Take care: HTML 5 validation do not work together with validation groups.

使用嵌入的表单和验证组可能会非常痛苦:Annotation @Assert\Valid()对我不适用(没有组就可以)。在默认情况下插入'cascade_validation' => true是关键。您不需要在->add()上重复此操作。注意:HTML 5验证不能与验证组一起工作。

Example:

例子:

A Collection of 2 Addresses. Both 1:1 unidirectional. Each with a different (!) validation group.

2个地址的集合。1:1单向。每个都有一个不同的(!)验证组。

  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;

//(...)
}

Address Entity

地址的实体

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;

Address type - ability to change validation group

地址类型-更改验证组的能力

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

And last the CollectionType

最后的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
    ));
}

//(...)    

Hope it helps..

希望它能帮助. .

#5


3  

You have to add validation_groups in your ProfiletType also. Validation is done in each form type separately based on their data_class if exists.

还必须在ProfiletType中添加validation_groups。如果存在,则根据data_class分别对每个表单类型进行验证。

#6


3  

are you using YML or Annotations?

你是使用YML还是注解?

I tried applying the cascade_validation option on my parent form class, but validation was still not occurring. After reading a little documentation, I went to app/config/config.yml and found that enable_annotations under framework->validation was set to true. Apparently, if this is true, the validation service no loner reads any validation.yml files. So I just changed it to false, and now the form is validating fine.

我尝试将cascade_validation选项应用到父窗体类上,但是验证仍然没有发生。在阅读了一些文档之后,我转到app/config/config。yml发现框架->验证下的enable_annotation被设置为true。显然,如果这是正确的,验证服务就不会有孤独者读取任何验证。yml文件。所以我把它改成了false,现在表单可以验证了。

#7


3  

I was looking for the exact same thing and here is what I found

我寻找的是完全一样的东西,这是我找到的。

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

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

You need to tell the main entity to validate it's sub entities like so:

你需要告诉主实体来验证它的子实体如下:

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

I've tested this on symfony 2.8 and it works.

我在symfony 2.8上测试过,它是有效的。

#8


0  

From my controller:

从我的控制器:

$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();

Fourth parametr in ::createNamedBuilder - array('cascade_validation' => true))

第四个参数::creat珐琅生成器-数组('cascade_validation' => true)

#1


111  

I spent an age searching and found that it was adding 'cascade_validation' => true to the setDefaults() array in my parent type's class that fixed it (as mentioned already in the thread). This causes the entity constraint validation to trigger in the child types shown in the form. e.g.

我花了很长时间搜索,发现它添加了'cascade_validation' => true给我父类型的类中的setDefaults()数组来修复它(正如在线程中已经提到的)。这导致实体约束验证在表单中显示的子类型中触发。如。

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

For collections, also make sure to add 'cascade_validation' => true to the $options array for the collection field on the form. e.g.

对于集合,还要确保在表单上的集合字段的$options数组中添加'cascade_validation' =>。如。

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

This will have the UniqueEntity validation take place as it should in the child entity used in the collection.

这将使UniqueEntity验证发生在集合中使用的子实体中。

#2


52  

A note to those using Symfony 3.0 and up: the cascade_validation option has been removed. Instead, use the following for embedded forms:

请注意使用Symfony 3.0和以上版本的用户:cascade_validation选项已经被删除。相反,在嵌入式表单中使用以下内容:

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

Sorry for adding to this old thread with a slightly off-topic answer (Symfony 3 vs. 2), but finding this information here would have saved me a few hours today.

很抱歉在这个旧的线程中添加了一个稍微偏离主题的答案(Symfony 3 vs. 2),但是在这里找到这些信息将会为我今天节省几个小时。

#3


33  

According to form type documentation you can also use Valid constraint instead of cascade_validation option.

根据表单类型文档,您还可以使用有效约束而不是cascade_validation选项。

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

Example from the owner entity:

业主实体的例子:

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

#4


4  

Belongs to Symfony 2.3

属于Symfony 2.3

Working with embedded forms and validation groups could be quite painful: Annotation @Assert\Valid() doesen't work for me (without groups it is ok). Insert 'cascade_validation' => true on the DefaultOptions is the key. You do not need to repeat this on the ->add(). Take care: HTML 5 validation do not work together with validation groups.

使用嵌入的表单和验证组可能会非常痛苦:Annotation @Assert\Valid()对我不适用(没有组就可以)。在默认情况下插入'cascade_validation' => true是关键。您不需要在->add()上重复此操作。注意:HTML 5验证不能与验证组一起工作。

Example:

例子:

A Collection of 2 Addresses. Both 1:1 unidirectional. Each with a different (!) validation group.

2个地址的集合。1:1单向。每个都有一个不同的(!)验证组。

  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;

//(...)
}

Address Entity

地址的实体

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;

Address type - ability to change validation group

地址类型-更改验证组的能力

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

And last the CollectionType

最后的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
    ));
}

//(...)    

Hope it helps..

希望它能帮助. .

#5


3  

You have to add validation_groups in your ProfiletType also. Validation is done in each form type separately based on their data_class if exists.

还必须在ProfiletType中添加validation_groups。如果存在,则根据data_class分别对每个表单类型进行验证。

#6


3  

are you using YML or Annotations?

你是使用YML还是注解?

I tried applying the cascade_validation option on my parent form class, but validation was still not occurring. After reading a little documentation, I went to app/config/config.yml and found that enable_annotations under framework->validation was set to true. Apparently, if this is true, the validation service no loner reads any validation.yml files. So I just changed it to false, and now the form is validating fine.

我尝试将cascade_validation选项应用到父窗体类上,但是验证仍然没有发生。在阅读了一些文档之后,我转到app/config/config。yml发现框架->验证下的enable_annotation被设置为true。显然,如果这是正确的,验证服务就不会有孤独者读取任何验证。yml文件。所以我把它改成了false,现在表单可以验证了。

#7


3  

I was looking for the exact same thing and here is what I found

我寻找的是完全一样的东西,这是我找到的。

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

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

You need to tell the main entity to validate it's sub entities like so:

你需要告诉主实体来验证它的子实体如下:

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

I've tested this on symfony 2.8 and it works.

我在symfony 2.8上测试过,它是有效的。

#8


0  

From my controller:

从我的控制器:

$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();

Fourth parametr in ::createNamedBuilder - array('cascade_validation' => true))

第四个参数::creat珐琅生成器-数组('cascade_validation' => true)