2013-05-10 8 views
5

In Symfony2, utilizzo i vincoli personalizzati per convalidare alcuni dati sul mio modulo, ma mi chiedo se posso introdurre un valore dal modulo da utilizzare per convalidare un altro valore?symfony2 convalida un valore di un modulo utilizzando un altro valore di forma

Ecco il mio vincolo ...

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

} 

E il mio validatore ...

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

utilizzate tramite un servizio ...

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

Ecco come mi applico come questo alla mia entità ...

<?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; 

Nel mio validatore mi piacerebbe essere in grado di realizzare qualcosa di simile, invece

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

risposta

12

È possibile creare un convalida del vincolo di classe (http://symfony.com/doc/master/cookbook/validation/custom_constraint.html#class-constraint-validator) e con questo si ottiene l'oggetto stesso.

In primo luogo è necessario creare una classe 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; 
} 
} 

dopo che creare validatore stesso in cui si ha accesso all'oggetto e non solo una singola proprietà.

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

      } 

     } 

    } 

quindi creare il servizio per accedere gestore di entità e roba del genere:

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

Anche in questo caso il validatore vincolo di classe viene applicato alla classe stessa, e non alla proprietà è necessario aggiungere il seguente annotazione alla tua classe.

use YourBundleName\Validator\Constraints as BackendAssert; 

/** 
* @BackendAssert\ContainerExists 
*/ 
+0

Grazie mille, mi ha aiutato molto. –

+0

felice di aiutare :) –

+0

Symfony 2.4 è molto più flessibile su questo punto ora. Dai un'occhiata a questo post: http://symfony.com/blog/new-in-symfony-2-4-a-better-callback-constraint – Maxooo

3

Questo potrebbe non essere la risposta che stai cercando, ma una possibile aggirare sarebbe quello di usare gli eventi di forma per la convalida :

$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

Questo va nella mia forma, nel mio caso ContainerType? –

+0

Sì, oppure puoi creare un tipo di modulo separato per la combinazione di campi e includerlo nel modulo –

0

Sembra che si desideri convalidare l'intera entità anziché un solo campo. È possibile utilizzare class constraint validator per questo. Ora la funzione validate accetta l'entità come primo argomento e puoi utilizzare tutti i campi entità.

0

Questo può essere fatto con un Assert \ Callback. Nel callback si ha accesso ai valori del modulo e si possono eseguire controlli con altri valori. Ecco una soluzione generale:

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

Questo associerà l'errore al campo di input specifico.

Problemi correlati