2013-05-10 97 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) 
} 

這會將錯誤與特定的輸入字段相關聯。