75

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

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

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

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

下面的代码:

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

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

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

8 回答 8

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

于 2013-06-21T14:29:14.410 回答
81

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

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

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

于 2016-06-14T13:17:51.870 回答
36

根据表单类型文档,您还可以使用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
于 2012-12-20T14:19:27.450 回答
4

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

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

您需要告诉主实体来验证它的子实体,如下所示:

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

我已经在 symfony 2.8 上对此进行了测试,并且可以正常工作。

于 2015-12-16T14:13:37.327 回答
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
    ));
}

//(...)    

希望能帮助到你..

于 2014-11-21T12:49:33.443 回答
3

你用的是 YML 还是注解?

我尝试cascade_validation在我的父表单类上应用该选项,但仍然没有进行验证。在阅读了一些文档后,我去app/config/config.yml发现enable_annotationsunderframework->validation被设置为true。显然,如果这是真的,验证服务不会读取任何validation.yml 文件。所以我只是将其更改为false,现在表单验证正常。

于 2013-04-09T18:34:54.923 回答
2

validation_groups您还必须添加ProfiletType。根据data_class是否存在,分别在每种表单类型中进行验证。

于 2012-04-13T10:47:33.297 回答
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))

于 2013-08-11T20:21:46.473 回答