Symfony2在Data Transformer之前形成字段约束验证

时间:2014-07-15 07:46:54

标签: forms validation symfony

我已经创建了需要数据转换器的表单,但是让我自己陷入单一问题:我通过爆炸字符串转换数据(字符串应该被分解为3个部分),一切正常,如果我提供正确的格式字符串,否则它会抛出数据转换器内部的错误,因为如果提供了错误的字符串格式,则不会发生转换(这是预期的行为)。

所以问题是有没有办法在数据转换之前验证表单字段是否正确的字符串?我知道默认情况下,数据转换会在验证之前发生,但也许有其他方法可以做到这一点吗?

我找到了一个可能适用于此主题的解决方案:Combine constraints and data transformers, 但它看起来像粗略的解决方案,除了我需要翻译验证消息,我真的想使用symfony表单的默认翻译方法(不使用翻译服务)

我想,也有来自symfony IRC(Iltar)的人建议通过使用事件来做到这一点,但我不知道如何解决这个问题 - 如何动态地将数据转换器附加到表单字段?或者可能还有其他方式?

3 个答案:

答案 0 :(得分:6)

可能为时已晚,但我最终设法做到了。 也许它会帮助你。

这是我的FormType:

class PersonType extends AbstractType{

    public function buildForm(FormBuilderInterface $builder, array $options){
        $builder->add('mother', 'personSelector', array('personEntity' => $options['personEntity']));

    }
}

这是我的customField,其中包含验证:

class PersonSelectorType extends AbstractType{

    public function buildForm(FormBuilderInterface $builder, array $options){
        $transformer = new PersonByFirstnameAndLastnameTransformer($this->entityManager,$options);
        $builder->addModelTransformer($transformer);
        $builder->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmitForm'));
    }

    public function onPreSubmitForm(FormEvent $event){
        $mother     = $event->getData();
        $form       = $event->getForm();
        $options    = $form->getConfig()->getOptions();
        if (!empty($mother)){
            preg_match('#(.*) (.*)#', $mother, $personInformations);
            if (count($personInformations) != 3){
                $form->addError(new FormError('[Format incorrect] Le format attendu est "Prénom Nom".'));
            }else{
                $person = $this->entityManager->getRepository($options['personEntity'])->findOneBy(array('firstname' => $personInformations[1],'lastname' =>$personInformations[2]));
                if ($person === null) {
                    $form->addError(new FormError('Il n\'existe pas de person '.$personInformations[1].' '.$personInformations[2].'.'));
                }
            }
        }
    }
}

这是我的变压器:

class PersonByFirstnameAndLastnameTransformer implements DataTransformerInterface{

    public function reverseTransform($firstnameAndLastname) {
        if (empty($firstnameAndLastname)) { return null; }
        preg_match('#(.*) (.*)#', $firstnameAndLastname, $personInformations);
        $person = $this->entityManager->getRepository($this->options['personEntity'])->findOneBy(array('firstname' =>$personInformations[1],'lastname' =>$personInformations[2]));
        if (count($personInformations) == 3){
            $person = $this->entityManager->getRepository($this->options['personEntity'])->findOneBy(array('firstname' =>$personInformations[1],'lastname' =>$personInformations[2]));
        }
        return $person;
    }

    public function transform($person) {
        if ($person === null) { return ''; }
        return $person->getFirstname().' '.$person->getLastname();
    }
}

答案 1 :(得分:0)

也许您可以将表单实例传递给变压器。如果字符串无法正确解析,只需向表单添加验证错误,如下所示:

<?php
// src/Acme/MyBundle/Form/DataTransformer/StringTransformer.php
namespace Acme\MyBundle\Form\DataTransformer;

use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\Common\Persistence\ObjectManager;
use Acme\MyBundle\Entity\MyEntity;
use Acme\MyBundle\Entity\AnotherEntity;
use Acme\MyBundle\Type\MyEntityType;

class StringTransformer implements DataTransformerInterface
{
  /**
   * @var MyEntityType
   */
  private $form;

  /**
   * @param ObjectManager $om
   */
  public function __construct(MyEntityType $form)
  {
    $this->form = $form;
  }

  /**
   * Transforms an object (entity) to a string (number).
   *
   * @param  MyEntity|null $entity
   * @return string
   */
  public function transform($value)
  {
    // ...
  }

  /**
   * Transforms a string (number) to an object (entity).
   *
   * @param  string $number
   *
   * @return MyEntity|null
   *
   * @throws TransformationFailedException if object (entity) is not found.
   */
  public function reverseTransform($value)
  {
    $collection = new ArrayCollection();

    try{
      $vals = explode(',', $value);

      foreach($vals as $v){
        $entity = new AnotherEntity();
        $entity->setValue($v);
        $collection->add($v);
      }

    } catch(\Exception $e){
      $this->form
        ->get('my_location')
        ->addError(new FormError('error message'));
    }

    return $collection;
  }
}

答案 2 :(得分:0)

  

但是它看起来像一个粗糙的解决方案,除了我需要翻译验证消息,而且我真的很想使用symfony表单的默认翻译方法(不使用翻译服务)

我知道这个问题很旧,但是由于任何答案都被标记为正确的解决方案,因此我与您分享另一种方法。

emottet解决方案是一种很好的方法,它基于this discussion,使用预先提交的侦听器在应用模型转换器之前验证数据。

如果您也想继续使用Symfony验证系统来解决这些错误,则可以在提交前的侦听器中使用Symfony验证器服务(ValidatorInterface)并将所需的约束传递给它,例如:

$builder
    ->add('whatever1', TextType::class)
    ->add('whatever2', TextType::class)
;
$builder->get('whatever1')
    ->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
        $data = $event->getData();
        $form = $event->getForm();

        /** @var ConstraintViolationListInterface $errors */
        if ($errors = $this->validator->validate($data, new Choice([
            'choices' => $allowedChoices,
            'message' => 'message.in.validators.locale.xlf'
        ]))) {
            /** @var ConstraintViolationInterface $error */
            foreach ($errors as $error) {
                $form->addError(new FormError($error->getMessage()));
            }
        }
    })
    ->addModelTransformer($myTransformer)
;

有点多余,但是可以。更多信息here