php Symfony2 - 验证不适用于嵌入式表单类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10138505/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 21:28:58  来源:igfitidea点击:

Symfony2 - Validation not working for embedded Form Type

phpsymfony

提问by Mr Pablo

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 中。表单正确呈现并显示正确的信息,因此它似乎已正确连接到 Profile 实体。这只是在 ProfileType 上被破坏的验证。

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

关于为什么一部分会验证而另一部分不会验证的任何想法?

Code below:

代码如下:

Validation.yml

验证.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

用户类型.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

配置文件类型.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()));

回答by daftv4der

I spent an age searching and found that it was adding 'cascade_validation' => trueto 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' => truesetDefaults()我的父类型的类中的数组中来修复它(如线程中已经提到的)。这会导致实体约束验证在表单中显示的子类型中触发。例如

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

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

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

$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 验证。

回答by cg.

A note to those using Symfony 3.0and up: the cascade_validationoption 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)添加到这个旧线程中,但是今天在这里找到这些信息可以为我节省几个小时。

回答by supernova

According to form type documentationyou can also use Validconstraint instead of cascade_validationoption.

根据表单类型文档,您还可以使用Valid约束而不是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

回答by targnation

are you using YML or Annotations?

你在使用 YML 还是 Annotations?

I tried applying the cascade_validationoption on my parent form class, but validation was still not occurring. After reading a little documentation, I went to app/config/config.ymland found that enable_annotationsunder framework->validationwas 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_annotationsunderframework->validation设置为true。显然,如果这是真的,验证服务就不会读取任何validation.yml 文件。所以我只是将其更改为false,现在表单验证正常。

回答by Hauke

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.

使用嵌入式表单和验证组可能会非常痛苦:注释 @Assert\Valid() 对我不起作用(没有组也可以)。在 DefaultOptions 上插入 '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..

希望能帮助到你..

回答by nacholibre

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 上测试过它并且它有效。

回答by Mun Mun Das

You have to add validation_groupsin your ProfiletTypealso. Validation is done in each form type separately based on their data_classif exists.

你也必须加入validation_groups你的ProfiletType。验证是根据它们data_class是否存在分别在每个表单类型中完成的。

回答by d3uter

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))

::createNamedBuilder 中的第四个参数 - array('cascade_validation' => true))