【问题标题】:symfony2 validate a form value using another form valuesymfony2 使用另一个表单值验证表单值
【发布时间】:2013-05-10 08:43:39
【问题描述】:

在 Symfony2 中,我使用自定义约束来验证表单上的一些数据,但我想知道是否可以从表单中引入一个值来验证另一个值?

这是我的约束...

<?php
// src\BizTV\ContainerManagementBundle\Validator\Constraints\ContainerExists.php
namespace BizTV\ContainerManagementBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class ContainerExists extends Constraint
{
    public $message = 'Namnet är upptaget, vänligen välj ett annat.';

    public function validatedBy()
    {
        return 'containerExists';
    }

}

还有我的验证器...

<?php 
// src\BizTV\ContainerManagementBundle\Validator\Constraints\ContainerExistsValidator.php
namespace BizTV\ContainerManagementBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use Doctrine\ORM\EntityManager as EntityManager;

class ContainerExistsValidator extends ConstraintValidator
{

    private $container;
    private $em;

    public function __construct(Container $container, EntityManager $em) {
        $this->container = $container;
        $this->em = $em;
    }   

    public function isValid($value, Constraint $constraint)
    {

        $em = $this->em;
        $container = $this->container;

        $company = $this->container->get('security.context')->getToken()->getUser()->getCompany();

        //Fetch entities with same name
        $repository = $em->getRepository('BizTVContainerManagementBundle:Container');
        $query = $repository->createQueryBuilder('c')
            ->where('c.company = :company')
            ->setParameter('company', $company)
            ->orderBy('c.name', 'ASC')
            ->getQuery();
        $containers = $query->getResult();      

        foreach ($containers as $g) {
            if ($g->getName() == $value) {
                $this->setMessage('Namnet '.$value.' är upptaget, vänligen välj ett annat', array('%string%' => $value));
                return false;
            }
        }

        return true;
    }
}

通过服务使用...

services:
  biztv.validator.containerExists:
    class: BizTV\ContainerManagementBundle\Validator\Constraints\ContainerExistsValidator
    arguments: ['@service_container', '@doctrine.orm.entity_manager']      
    tags:
        - { name: validator.constraint_validator, alias: containerExists }

这是我如何将它应用到我的实体...

<?php

namespace BizTV\ContainerManagementBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

use BizTV\ContainerManagementBundle\Validator\Constraints as BizTVAssert;

use BizTV\UserBundle\Entity\User as user;
use BizTV\ContainerManagementBundle\Entity\Container as Container;

/**
 * BizTV\ContainerManagementBundle\Entity\Container
 *
 * @ORM\Table(name="container")
 * @ORM\Entity
 */
class Container
{

    /**
     * @var integer $id
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string $name
     * @Assert\NotBlank(message = "Du måste ange ett namn") 
     * @BizTVAssert\ContainerExists
     * @ORM\Column(name="name", type="string", length=255, nullable=true)
     */
    private $name;

在我的验证器中,我希望能够完成类似的事情

public function isValid($FORM->OTHERVALUE, $value, Constraint $constraint)
{

    $em = $this->em;
    $container = $this->container;

    $company = $this->container->get('security.context')->getToken()->getUser()->getCompany();

    //Fetch entities with same name
    $repository = $em->getRepository('BizTVContainerManagementBundle:Container');
    $query = $repository->createQueryBuilder('c')
        ->where('c.company = :company')
        ->setParameter('company', $company)
        ->orderBy('c.name', 'ASC')
        ->getQuery();
    $containers = $query->getResult();      


        if ($g->getName() == $FORM->OTHERVALUE) {
            $this->setMessage('Namnet '.$value.' är upptaget, vänligen välj ett annat', array('%string%' => $value));
            return false;
        }


    return true;
}

【问题讨论】:

    标签: forms validation symfony


    【解决方案1】:

    您可以创建一个类约束验证器 (http://symfony.com/doc/master/cookbook/validation/custom_constraint.html#class-constraint-validator),然后您就可以得到对象本身。

    首先你需要创建一个约束类:

    class ContainerExists extends Constraint
    {
        public $message = 'Namnet är upptaget, vänligen välj ett annat.';
    
        public function validatedBy()
        {
            return 'containerExists';
        }
    
        public function getTargets()
    {
        return self::CLASS_CONSTRAINT;
    }
    }
    

    之后创建验证器本身,您可以在其中访问对象,而不仅仅是单个属性。

    class ContainerExistsValidator extends ConstraintValidator {
    
        private $em;
        private $security_context;
    
        public function __construct(EntityManager $entityManager, $security_context) {
            $this->security_context = $security_context;
            $this->em = $entityManager;
        }
    
        public function validate($object, Constraint $constraint) {
    
            // do whatever you want with the object, and if something is wrong add a violation
    
                    $this->context->addViolation($constraint->message, array('%string%' => 'something'));
    
                }
    
            }
    
        }
    

    然后创建服务来访问实体管理器和东西:

    validator.container_exists:
            class: YourBundleName\Validator\Constraints\ContainerExistsValidator
            arguments: ["@doctrine.orm.entity_manager", "@security.context"]
            tags:
                - { name: validator.constraint_validator, alias: containerExists }
    

    在这种情况下,由于类约束验证器应用于类本身,而不是应用于需要将以下注释添加到类的属性。

    use YourBundleName\Validator\Constraints as BackendAssert;
    
    /**
     * @BackendAssert\ContainerExists
     */
    

    【讨论】:

    • 非常感谢,帮了我很大的忙。
    • Symfony 2.4 现在在这一点上更加灵活。看看这个帖子:symfony.com/blog/…
    【解决方案2】:

    这可能不是您正在寻找的答案,但一种可能的解决方法是使用表单事件进行验证:

        $builder->addEventListener(FormEvents::POST_BIND, function (DataEvent $event) {
            $form = $event->getForm();
    
            // You can access all form values with $form['value']
            if ($form['name'] == $form['OTHERVALUE']) {
    
              $form['name']->addError(new FormError('Your Message'));
    
            }
    
        });
    

    【讨论】:

    • 这是否在我的表单中,在我的情况下是 ContainerType?
    • 是的,或者您可以为字段组合创建单独的表单类型并将其包含在您的表单中
    【解决方案3】:

    这可以通过 Assert\Callback 来完成。在回调中,您可以访问表单值并可以使用其他值执行检查。这是一个通用的解决方案:

    use Symfony\Component\Validator\Constraints as Assert;
    use Symfony\Component\Form\Extension\Core\Type\TextType;
    use Symfony\Component\Validator\Context\ExecutionContextInterface;
    
    
    public static function authenticate($val1, ExecutionContextInterface $context, $payload)
    {
         $testVal = null;
         $root = $context->getRoot();
         if ($root instanceof \Symfony\Component\Form\Form) {
             $testVal = $root->getViewData()['testVal'];
             if ($testVal < 5){
                 $context->buildViolation('Please enter a larger value')
                     ->atPath('val1')
                     ->addViolation()
                 ;
             }
         }
    }
    
    
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
         ->add('val1', TextType::class, [
                    'constraints' => [
                        new Assert\Callback([$this, 'authenticate']) //this calls the method above
                    ]
                ])
         ->add('testVal', TextType::class)
    }
    

    这会将错误与特定的输入字段相关联。

    【讨论】:

      【解决方案4】:

      看起来您想要验证整个实体而不是只验证一个字段。您可以为此使用class constraint validator。现在validate 函数将实体作为第一个参数,您可以使用所有实体字段。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多