4

For now I've successful used validation groups, but now I'm stuck with validation groups and nested mapped entities.

I'll explain the problem by a simplified example.

My entities: Address, Damage, Appliance

/**
 * @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;

    ...

To define my forms I use a AddressType, DamageType and 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;
                    }
        ));
    }

I'm adding the damage_responsible_address group when responsible is set to true in the form. Otherwise I don't want the address to be validated.

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;
                    }
        ));
    }

Same as previous, when toRepair is true I want to validate the address.

What's going wrong ?

I the Damage responsible is true and the appliance toRepair is false, the form does give validation errors on the responsible address BUT also on the the appliance address.

The same for the other way arround: When an appliance address is invalid (toRepar is true), then the responsibleAddress is also invalid (even when responsible is false).

The address validation groups don't look on which form they are defined, but just attatch them to every address item in the form.

Is it possible to define validation groups specific for a form only?

I am using Doctrine and Symfony 2.3.6.

3
  • Did you have any success ? Commented Jan 30, 2014 at 18:02
  • it would be nice if for such important questions you would provide feedback. Commented Jan 31, 2014 at 16:10
  • I fixed it by using a in class constraint on each Entity that want's the address to be valid. Cause some of them don't need to be validated. As @forgottenbas already said, it's OR validation which doesn't make this work. If once the group is applied on Address, it will be used for all Address Entities in the form. Commented Feb 6, 2014 at 11:58

3 Answers 3

1

The problem is that symfony uses OR-logic for validation groups. So then your apply one of groups to form it will validate address in both cases.

If you move the groups to a parent entity, it will not solve the problem?

/**
 * @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;
}
Sign up to request clarification or add additional context in comments.

1 Comment

They do not use OR-logic, and the problem is not that they validate in both case, but that all addresses invalidate, non ?
1

italic is for general case, bold for the particular case of this question.

The address validation groups don't look on which form they are defined, but just attach them to every address item in the form.

True, your validation_group callback in DamageType set validation_group for all the form, and as you use cascade_validation, for all embed AddressType the form contains. There is no reason it should understand that you want it set only for responsible address.

Generally, setting a validation group through callback for a parent form, with cascade validation set to true, will make all child forms validated against this validation group. If we want to set different validation groups for each children, we have to put a validation group callback in children formtype.

You must place your validation callback in AddressType, so it will be called for each Address Form. Of course, in order to do so, each Adress entity must be aware of its owner.

An alternative to validation callback is Group Sequence Provider. But in any case you still have to set your validation groups in each Address entity and not in Damage, so your Adress must still be aware of its owner.

2 Comments

By the way @gremo are you going to award your bounties ?
This post really helped me. I had groups set for a parent form, forcing the wrong validation on a child form even when default validation groups where set in the form. The solution was to either move simple validation into a constraint in the relevant form class, or use a specific callback within the child form type.
1

I know it has been a while, but here is the documentation that may answer the problem with symfony v3:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.