symfony2使用另一个表单值验证表单值

时间:2013-05-10 08:43:40

标签: forms validation symfony

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

4 个答案:

答案 0 :(得分:12)

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

首先,您需要创建一个Constraint类:

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
 */

答案 1 :(得分: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'));

        }

    });

答案 2 :(得分:0)

您希望验证整个实体而不是仅一个字段。您可以使用class constraint validator。现在validate函数将实体作为第一个参数,您可以使用所有实体字段。

答案 3 :(得分:0)

这可以通过 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)
}

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