【问题标题】:Symfony2 - Validation not working for embedded Form TypeSymfony2 - 验证不适用于嵌入式表单类型
【发布时间】:2012-04-25 16:19:43
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: php symfony


    【解决方案1】:

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

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

    【讨论】:

    • 您是如何确定需要将级联标志添加到集合的选项数组的?这是我的问题,但我在 Symfony 文档中没有遇到过?
    • 通过在这里筛选与类似问题相关的许多其他 cmets:/
    • 更容易的是,您可以在validation.yml 中指定“有效”约束,以启用对作为属性嵌入到正在验证的对象上的对象进行验证symfony.com/doc/current/reference/constraints/Valid.html
    • 对于那些使用 Symfony 3.0 及以上版本的用户:cascade_validation 已被删除。有关详细信息,请参阅下面的答案。
    • @sdespont 如果一个实体扩展了另一个实体,其中定义了带有Assert\Valid() 注释的属性,则第一个实体将不会对该属性进行级联验证。所以cascade_validation解决问题。
    【解决方案2】:

    使用 Symfony 3.0 及更高版本的用户注意:cascade_validation 选项 has been removed。相反,对嵌入式表单使用以下内容:

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

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

    【讨论】:

    • 太棒了,谢谢。经过验证的答案也应该包括您的答案。
    • 这是 Symfony 3.1.4 中唯一适合我的选项。遵循文档 symfony.com/doc/current/form/embedded.html 并将 @Assert\Valid() Constrait 添加到 Entity 就像它所写的那样不起作用。就我而言,我使用与 UserProfile (OneToOne) 相关的 FOSUserBundle 和自定义用户实体。谢谢@cg。
    • 有了这个,你可能想关闭error bubbling
    • 该死的,花了一些时间寻找这个.... @Assert\Valid 那么的目的是什么 -.- ... 这些类型的黑客是一个真正的关闭现在我们不得不担心关于另外两个地方的验证。非常感谢@cg。 !
    • 另外我必须在表单定义文件的顶部添加use Symfony\Component\Validator\Constraints\Valid;。 Symfony 4 在这里。
    【解决方案3】:

    根据form type documentation,你也可以使用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
    

    【讨论】:

    【解决方案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 上测试过了,它可以工作。

    【讨论】:

      【解决方案5】:

      您使用的是 YML 还是 Annotations?

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

      【讨论】:

        【解决方案6】:

        属于 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
            ));
        }
        
        //(...)    
        

        希望对你有帮助..

        【讨论】:

        • 这拯救了我的一天!!不..它节省了我的一年。上帝保佑你:D 你是否找到了 @Assert\Valid() 的解决方案,因为 'cascade_validation' 选项自 2.8 版以来已被弃用
        【解决方案7】:

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

        【讨论】:

        • 我后来补充说,它没有帮助。但我找到了解决办法。我必须将“cascade_validation”添加到选项数组中。太烦人了,因为 Symfony2 文档中的任何地方都没有提到这一点。
        • @MrPablo 我想这是 2.1 中的 cascade_validation 选项?
        • @MrPablo 既然您似乎已经找到了问题的答案,请考虑发布您自己的答案以及使用“cascade_validation”的完整示例。这一点太重要了,不能在评论中降级!谢谢
        【解决方案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))

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-05-20
          • 2016-09-28
          • 1970-01-01
          • 2012-08-10
          • 1970-01-01
          • 2017-08-04
          • 1970-01-01
          相关资源
          最近更新 更多