9

私のフォームは次のようになります。

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他のフィールドの1つに変更した場合は、正常に機能します。

このエラーを適切な場所に表示するにはどうすればよいですか?エラーがコレクション内の単一のエンティティにあり、子形式でレンダリングされた場合は正常に機能すると思いますが、コレクション内のどのエンティティもアクティブとしてマークされていない場合は違反が発生するため、必要です。親レベルになります。

4

3 に答える 3

24

デフォルトでは、フォームのオプション「error_bubbling」がに設定されていますtrue。これにより、今説明した動作が発生します。エラーを保持したい場合は、個々のフォームに対してこのオプションをオフにすることができます。

$builder->add('departments', 'collection', array(
    'type' => new Department,
    'error_bubbling' => false,
));
于 2012-07-31T16:42:47.883 に答える
4

私はSymfony3.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;

                }
            }
        }
    }
}

これにより、添付画像に従って、コレクションの要素のフィールドにエラーが追加されます。 要素/フィールドでのエラー

クレイグ

于 2017-10-27T04:35:45.543 に答える
0

非常によく似たケースがあります。カスタムフォーム(内部にDataTransformersなどがある)を持つCollectionTypeがあります。要素を1つずつチェックし、それらの何が間違っているかをマークして、ビューに印刷する必要があります。

私は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を使用して違反を作成します。

于 2018-02-20T11:09:54.037 に答える