我的表单如下:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$factory = $builder->getFormFactory();
$builder->add('name');
$builder->add('description');
$builder->add('manufacturers', null, array(
'required' => false
));
$builder->add('departments', 'collection', array(
'type' => new Department
));
}
我在表单所代表的实体上有一个类验证器,它调用:
if (!$valid) {
$this->context->addViolationAtSubPath('departments', $constraint->message);
}
只会在表单中添加“全局”错误,而不会在子路径中添加错误。我认为这是因为departments是一个嵌入另一个FormType的集合。
如果我将departments
更改为其他字段之一,则可以正常工作。
如何才能将此错误显示在正确的位置?我假设如果我的错误是在集合中的单个实体上,并因此以子表单形式呈现,它将正常工作,但我的标准是如果集合中没有任何实体被标记为活动,则会发生违规,因此它需要在父母层面。
答案 0 :(得分:24)
默认情况下,表单将“error_bubbling”选项设置为true
,这会导致您刚才描述的行为。如果您希望他们保留错误,您可以为单个表单关闭此选项。
$builder->add('departments', 'collection', array(
'type' => new Department,
'error_bubbling' => false,
));
答案 1 :(得分:2)
我在Symfony 3.3中一直在努力解决这个问题,我希望验证整个集合,但是将错误传递给相应的集合元素/字段。该集合将添加到表单中:
$form->add('grades', CollectionType::class,
[
'label' => 'student.grades.label',
'allow_add' => true,
'allow_delete' => true,
'entry_type' => StudentGradeType::class,
'attr' => [
'class' => 'gradeList',
'help' => 'student.grades.help',
],
'entry_options' => [
'systemYear' => $form->getConfig()->getOption('systemYear'),
],
'constraints' => [
new Grades(),
],
]
);
StudentGradeType是:
<?php
namespace Busybee\Management\GradeBundle\Form;
use Busybee\Core\CalendarBundle\Entity\Grade;
use Busybee\Core\SecurityBundle\Form\DataTransformer\EntityToStringTransformer;
use Busybee\Core\TemplateBundle\Type\SettingChoiceType;
use Busybee\Management\GradeBundle\Entity\StudentGrade;
use Busybee\People\StudentBundle\Entity\Student;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class StudentGradeType extends AbstractType
{
/**
* @var ObjectManager
*/
private $om;
/**
* StaffType constructor.
*
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('status', SettingChoiceType::class,
[
'setting_name' => 'student.enrolment.status',
'label' => 'grades.label.status',
'placeholder' => 'grades.placeholder.status',
'attr' => [
'help' => 'grades.help.status',
],
]
)
->add('student', HiddenType::class)
->add('grade', EntityType::class,
[
'class' => Grade::class,
'choice_label' => 'gradeYear',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('g')
->orderBy('g.year', 'DESC')
->addOrderBy('g.sequence', 'ASC');
},
'placeholder' => 'grades.placeholder.grade',
'label' => 'grades.label.grade',
'attr' => [
'help' => 'grades.help.grade',
],
]
);
$builder->get('student')->addModelTransformer(new EntityToStringTransformer($this->om, Student::class));
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setDefaults(
[
'data_class' => StudentGrade::class,
'translation_domain' => 'BusybeeStudentBundle',
'systemYear' => null,
'error_bubbling' => true,
]
);
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'grade_by_student';
}
}
并且验证器看起来像:
namespace Busybee\Management\GradeBundle\Validator\Constraints;
use Busybee\Core\CalendarBundle\Entity\Year;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class GradesValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if (empty($value))
return;
$current = 0;
$year = [];
foreach ($value->toArray() as $q=>$grade)
{
if (empty($grade->getStudent()) || empty($grade->getGrade()))
{
$this->context->buildViolation('student.grades.empty')
->addViolation();
return $value;
}
if ($grade->getStatus() === 'Current')
{
$current++;
if ($current > 1)
{
$this->context->buildViolation('student.grades.current')
->atPath('['.strval($q).']') // could do a single atPath with a value of "[".strval($q)."].status"
->atPath('status') // full path = children['grades'].data[1].status
->addViolation();
return $value;
}
}
$gy = $grade->getGradeYear();
if (! is_null($gy))
{
$year[$gy] = empty($year[$gy]) ? 1 : $year[$gy] + 1 ;
if ($year[$gy] > 1)
{
$this->context->buildViolation('student.grades.year')
->atPath('['.strval($q).']')
->atPath('grade')
->addViolation();
return $value;
}
}
}
}
}
克雷格
答案 2 :(得分:0)
我的情况非常相似。我有一个带有自定义表单的CollectionType(里面有DataTransformers等等),我需要逐个检查元素并标记它们的错误并将其打印在视图上。
我在ConstraintValidator(我的自定义验证器)上制作了该解决方案:
验证程序必须以 CLASS_CONSTRAINT 为目标,或者propertyPath不起作用。
public function validate($value, Constraint $constraint) {
/** @var Form $form */
$form = $this->context->getRoot();
$studentsForm = $form->get("students"); //CollectionType's name in the root Type
$rootPath = $studentsForm->getPropertyPath()->getElement(0);
/** @var Form $studentForm */
foreach($studentsForm as $studentForm){
//Iterate over the items in the collection type
$studentPath = $studentForm->getPropertyPath()->getElement(0);
//Get the data typed on the item (in my case, it use an DataTransformer and i can get an User object from the child TextType)
/** @var User $user */
$user = $studentForm->getData();
//Validate your data
$email = $user->getEmail();
$user = $userRepository->findByEmailAndCentro($email, $centro);
if(!$user){
//If your data is wrong build the violation from the propertyPath getted from the item Type
$this->context->buildViolation($constraint->message)
->atPath($rootPath)
->atPath(sprintf("[%s]", $studentPath))
->atPath("email") //That last is the name property on the item Type
->addViolation();
}
}
}
我只是再次验证集合中的表单元素,并使用集合中错误的项目中的propertyPath构建违规。