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


74

我有一个结合了两个实体(用户和个人资料)的表单。

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

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

关于为什么一部分可以验证而另一部分不能验证的任何想法吗?

代码如下:

验证文件

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

Answers:


117

我花了一个年龄进行搜索,发现它正在添加'cascade_validation' => truesetDefaults()修复它的父类型的类中的数组中(如线程中已经提到的)。这将导致实体约束验证触发表单中显示的子类型。例如

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

这将像在集合中使用的子实体中那样进行UniqueEntity验证。


3
您如何解决需要将级联标志添加到集合的options数组的问题?这是我的问题,但是我没有在Symfony文档中遇到它吗?
redbirdo 2013年

4
通过筛选有关此问题的许多其他评论:/
daftv4der 2013年

6
更容易地,您可以在validation.yml中指定“有效”约束,以对作为被验证对象的属性嵌入的对象启用验证symfony.com/doc/current/reference/constraints/Valid.html
sdespont

8
对于现在使用Symfony 3.0及更高版本的用户:删除了层叠验证。请参阅下面的详细信息。
cg。

@sdespont如果一个实体扩展了另一个Assert\Valid()定义了带注释属性的位置,则第一个实体将不会对该属性进行级联验证。因此cascade_validation解决问题。
paaacman

79

给使用Symfony 3.0及更高版本的用户的注释:该cascade_validation选项已被删除。而是,将以下内容用于嵌入式表单:

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

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


太好了,谢谢。经过验证的答案还应该包括您的答案。
2016年

3
这是Symfony 3.1.4中唯一适用于我的选项。遵循文档symfony.com/doc/current/form/embedded.html 并向实体添加@Assert \ Valid()Constrait就像写的那样不起作用。在我的情况下,我使用FOSUserBundle和与UserProfile(OneToOne)相关的自定义用户实体。谢谢@cg。
治愈85 '16

1
这样,您可能要关闭错误冒泡
Jorr.it

该死的,花了一些时间来寻找这个。...@Assert \ Valid的目的是什么-.- ...这些类型的骇客是一个真正的关闭,现在我们不得不担心另外两个地方的验证。非常感谢@cg。!
本杰明·维森

另外,我必须use Symfony\Component\Validator\Constraints\Valid;在表单定义文件的顶部添加。Symfony 4在这里。
NullIsNot0



3

您在使用YML还是注释?

我尝试将cascade_validation选项应用于父窗体类,但仍未进行验证。读小的文件后,我去了app/config/config.yml,发现enable_annotationsframework->validation被设置为。显然,如果为true,则验证服务no loner会读取任何validation.yml文件。所以我只是将其更改为false,现在该表单可以很好地验证。


3

属于Symfony 2.3

使用嵌入式表单和验证组可能会很痛苦:注释@Assert \ Valid()对我不起作用(没有组也可以)。在DefaultOptions上插入'cascade_validation'=> true是关键。您无需在-> add()上重复此操作。注意:HTML 5验证不能与验证组一起使用。

例:

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;

//(...)
}

地址实体

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;

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

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

//(...)    

希望能帮助到你..


1
这挽救了我的一天!不,这节省了我的时间。愿上帝保佑您:D您是否找到了@Assert \ Valid()解决方案,因为自2.8版以来已不推荐使用'cascade_validation'选项
Stasa

2

你必须添加validation_groups在你ProfiletType还。根据每种表单类型(data_class如果存在)分别进行验证。


6
我后来补充说,它没有帮助。但是我找到了解决办法。我必须在选项数组中添加“ cascade_validation”。该死的烦人,因为这在任何地方的Symfony2文档中都没有提到。
Pablo先生2012年

3
@MrPablo我认为这是cascade_validation2.1中的选项?
richsage

3
@MrPablo由于您似乎已找到问题的答案,因此请考虑使用“ cascade_validation”的完整示例发布您自己的答案。这个特殊性太重要了,不能发表评论!谢谢
JeanValjean 2012年

0

从我的控制器:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.