Группы проверки Symfony2 и сопоставление

На данный момент я успешно использовал группы проверки, но теперь я застрял с группами проверки и вложенными сопоставленными объектами.

Поясню проблему на упрощенном примере.

Мои объекты: Адрес, Повреждение, Устройство

/**
 * @ORM\Entity()
 */
class Address extends ...
{
    /**
     * @var string
     * @ORM\Column(type="string", name="postcode", nullable=true)
     * @Assert\NotBlank(
     *     groups={
     *     "damage_responsible_address",
     *     "appliance_repairer_address",
     *     })
     */
    private $postcode;

    ...


/**
 * @ORM\Entity()
 */
class Damage extends ...
{
    /**
     * @var boolean
     * @ORM\Column(type="boolean", name="responsible", nullable=true)
     * @Assert\NotBlank(groups={"damage"})
     */
    private $responsible;

    /**
     * @ORM\OneToOne(targetEntity="Address", cascade={"persist","remove"})
     * @ORM\JoinColumn(name="responsible_address_id", referencedColumnName="id")
     * @Assert\Valid()
     */
    private $responsibleAddress;

    /**
     * @ORM\ManyToMany(targetEntity="Appliance", orphanRemoval=true, cascade={"persist", "remove"})
     * @ORM\JoinTable(name="coronadirect_cuzo_home_damage_appliances",
     *      joinColumns={@ORM\JoinColumn(name="damage_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="appliance_id", referencedColumnName="id")}
     *      )
     */
    private $appliances;

    ...


/**
 * @ORM\Entity()
 */
class Appliance extends ...
{
    /**
     * @var boolean
     * @ORM\Column(type="boolean", name="to_repair", nullable=true)
     * @Assert\NotBlank(groups={"appliance"})
     */
    private $toRepair;

     /**
     * @ORM\OneToOne(targetEntity="Address", cascade={"persist","remove"})
     * @ORM\JoinColumn(name="repairer_address_id", referencedColumnName="id")
     * @Assert\Valid()
     */
    private $repairAddress;

    ...

Для определения своих форм я использую AddressType, DamageType и ApplianceType:

class DamageType extends ...
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
         $builder->add('appliances', 'collection', array(
            'type' => 'home_damage_appliance_type',
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
            'options' => array(
                'cascade_validation' => true,
            )
        ));

        $builder->add('responsible', 'choice', array(
            'choices' => $this->getYesNoChoiceArray(),
            'expanded' => true,
            'multiple' => false,
        ));

        $builder->add('responsibleAddress', 'address_type', array(
            'required' => true
        ));

        ...
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
                'data_class' => 'Damage',
                'cascade_validation' => true,
                'validation_groups' =>
                    function(FormInterface $form) {

                        $groups = array('damage');

                        if ($form->getData()->getResponsible() == true) {
                            $groups[] = 'damage_responsible_address';
                        }

                        return $groups;
                    }
        ));
    }

Я добавляю группу damage_responsible_address, когда в форме для ответственного установлено значение true. В противном случае я не хочу, чтобы адрес проверялся.

class ApplianceType extends ...
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {

        $builder->add('toRepair', 'choice', array(
            'choices' => $this->getYesNoChoiceArray(),
            'expanded' => true,
            'multiple' => false,
        ));

        $builder->add('repairAddress', 'address_type', array(
            'required' => true
        ));

        ...
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
                'data_class' => 'Appliannce',
                'cascade_validation' => true,
                'validation_groups' =>
                    function(FormInterface $form) {

                        $groups = array('appliance');

                        if ($form->getData()->getToRepair() == true) {
                            $groups[] = 'appliance_repairer_address';
                        }

                        return $groups;
                    }
        ));
    }

Как и в предыдущем случае, когда toRepair имеет значение true, я хочу проверить адрес.

Что происходит?

Если Ответственность за ущерб верна, а устройство toRepair ложно, форма выдает ошибки проверки на ответственном адресе, НО также и на адресе устройства.

То же самое и в обратном случае: если адрес устройства недействителен (toRepar имеет значение true), то answerAddress также недействителен (даже если ответственный имеет значение false).

Группы проверки адресов не смотрят, в какой форме они определены, а просто прикрепляют их к каждому элементу адреса в форме.

Можно ли определить группы проверки только для формы?

Я использую Doctrine и Symfony 2.3.6.


person jayv    schedule 31.10.2013    source источник
comment
Были ли у вас успехи?   -  person jillro    schedule 30.01.2014
comment
было бы неплохо, если бы по таким важным вопросам вы давали обратную связь.   -  person jillro    schedule 31.01.2014
comment
Я исправил это, используя ограничение класса для каждой сущности, которая хочет, чтобы адрес был действительным. Потому что некоторые из них не нуждаются в подтверждении. Как уже сказал @forgottenbas, это проверка ИЛИ, которая не делает эту работу. Если однажды группа будет применена к адресу, она будет использоваться для всех объектов адреса в форме.   -  person jayv    schedule 06.02.2014


Ответы (3)


Проблема в том, что symfony использует ИЛИ-логику для групп проверки. Таким образом, вы примените одну из групп, чтобы сформировать ее, и она будет проверять адрес в обоих случаях.

Если вы переместите группы в родительскую сущность, это не решит проблему?

/**
 * @ORM\Entity()
 */
class Damage extends ...
{
    /**
     * @ORM\OneToOne(targetEntity="Address", cascade={"persist","remove"})
     * @ORM\JoinColumn(name="responsible_address_id", referencedColumnName="id")
     * @Assert\Valid(
     *     groups={
     *     "damage_responsible_address"
     *     })
     * )
     */
    private $responsibleAddress;
}
person Alexey B.    schedule 24.01.2014
comment
Они не используют логику ИЛИ, и проблема не в том, что они проверяются в обоих случаях, а в том, что все адреса недействительны, non ? - person jillro; 29.01.2014

курсив для общего случая, жирный для частного случая этого вопроса.

Группы проверки адресов не смотрят, в какой форме они определены, а просто прикрепляют их к каждому элементу адреса в форме.

Правда, ваш обратный вызов validation_group в DamageType устанавливает validation_group для всей формы, а поскольку вы используете cascade_validation, для всех встроенных AddressType, содержащихся в форме. Нет причин, по которым он должен понимать, что вы хотите установить его только для ответственного адреса.

Как правило, установка группы проверки с помощью обратного вызова для родительской формы с установленным для каскадной проверки значением true приведет к тому, что все дочерние формы будут проверены в соответствии с этой группой проверки. Если мы хотим установить разные группы проверки для каждого дочернего элемента, мы должны поместить обратный вызов группы проверки в дочерний тип формы.

Вы должны поместить обратный вызов для проверки в AddressType, чтобы он вызывался для каждой формы Address. Конечно, для этого каждый объект Address должен знать своего владельца.

Альтернативой обратному вызову проверки является поставщик групповой последовательности. Но в любом случае вам все равно придется устанавливать свои группы проверки в каждом объекте Address, а не в Damage, поэтому ваш адрес все равно должен знать о своем владельце.

person jillro    schedule 28.01.2014
comment
Кстати, @gremo, ты собираешься вручать свои награды? - person jillro; 31.01.2014
comment
Этот пост действительно помог мне. У меня были группы, установленные для родительской формы, что приводило к неправильной проверке дочерней формы, даже если в форме были установлены группы проверки по умолчанию. Решение состояло в том, чтобы либо переместить простую проверку в ограничение в соответствующем классе формы, либо использовать определенный обратный вызов в дочернем типе формы. - person Ben Stinton; 08.09.2014

Я знаю, что это было давно, но вот документация, которая может решить проблему с symfony v3:

http://symfony.com/doc/current/form/data_based_validation.html

person rmasclef    schedule 05.01.2017