Forms Symfony2数据转换器之前的表单字段约束验证
我已经创建了需要数据转换器的表单,但我陷入了一个问题:我通过分解字符串(字符串应该分解为3部分)来转换数据,如果我提供了正确的格式字符串,一切都可以工作,但否则它会在数据转换器内抛出错误,因为如果提供了错误的字符串格式,则无法进行转换(这是预期的行为) 所以问题是,在数据转换之前,有没有一种方法可以验证表单字段是否为正确的字符串?我知道默认情况下,数据转换发生在验证之前,但也许有其他方法可以做到这一点 我找到了一个可能适用于此线程的解决方案:, 但这看起来是一个粗略的解决方案,而且我需要翻译验证消息,我真的希望使用symfony表单的默认翻译方法(不使用翻译服务)Forms Symfony2数据转换器之前的表单字段约束验证,forms,validation,symfony,Forms,Validation,Symfony,我已经创建了需要数据转换器的表单,但我陷入了一个问题:我通过分解字符串(字符串应该分解为3部分)来转换数据,如果我提供了正确的格式字符串,一切都可以工作,但否则它会在数据转换器内抛出错误,因为如果提供了错误的字符串格式,则无法进行转换(这是预期的行为) 所以问题是,在数据转换之前,有没有一种方法可以验证表单字段是否为正确的字符串?我知道默认情况下,数据转换发生在验证之前,但也许有其他方法可以做到这一点 我找到了一个可能适用于此线程的解决方案:, 但这看起来是一个粗略的解决方案,而且我需要翻译验证
我想,也有来自symfony IRC(Iltar)的人建议通过使用事件来实现,但我不知道如何实现这一点——如何将数据转换器动态地附加到表单字段?或者有其他方法?也许您可以将表单的实例传递给转换器。如果字符串解析不正确,只需向表单中添加验证错误,如下所示:
<?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;
}
}
可能太晚了,但我终于做到了。 也许对你有帮助 这是我的表单类型:
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();
}
}
但这看起来是一个粗略的解决方案,而且我需要翻译验证消息,我真的希望使用symfony表单的默认翻译方法(不使用翻译服务)
我知道这个问题由来已久,但由于任何答案都被认为是正确的解决方案,我与你们分享另一种方法
解决方案是,在应用模型转换器之前,使用预提交侦听器验证数据,这是一个很好的方法,基于
如果您还想继续使用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)
;
有点多余,但它可以工作。更多信息。如果您仍然没有解决问题,请尝试检查以下问题:我很久以前确实解决了这个问题,使用了您建议的解决方案,但我没有回答这个问题,因为在我看来,这种解决方案非常庞大,并且违反了symfony的解耦代码原则,因为我需要手动使用翻译服务来翻译验证消息。如果要针对特定表单元素在
PersonSelectorType
中进行验证,是否不需要调用$event->setData($element)
?