2013-05-10 94 views
5

在Symfony2中,我使用自定义约束来验证表单上的某些数据,但是我是否可以从表单中引入一个值来验证其他值?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; 
} 

回答

12

您可以创建一个类约束验证器(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 
*/ 
+0

非常感谢,帮了我很多。 – 2013-06-04 22:07:17

+0

很高兴帮助:) – 2013-06-04 22:31:51

+0

现在Symfony 2.4在这一点上更加灵活。看看这篇文章:http://symfony.com/blog/new-in-symfony-2-4-a-better-callback-constraint – Maxooo 2014-03-21 09:00:39

3

某事,这可能不是你要找的答案,但一个可能的解决办法是要用于验证表单事件:

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

     } 

    }); 
+0

这是否在我的形式,在我的情况ContainerType? – 2013-05-29 14:15:01

+0

是的,或者您可以为字段组合创建单独的表单类型,并将其包含在您的表单中 – 2013-05-29 14:50:33

0

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

0

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

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

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