21

My application manage families. One family consist of 1 or N members.

I want the possibility to add one parent or two and 0 or N children. The children part works fine, but I have a hard time dealing with 1 or 2 parents.

Here is my family form type:

 $builder
        ... many attributes
        ->add('parent1', MemberType::class)
        ->add('parent2', MemberType::class)

Parent and parent2 are OneToOne association (Family to member). The member form type :

 $builder
        ->add('firstName', TextType::class, [
            'label' => 'Prénom',
            'constraints' => array(
                new NotBlank(),
                new Length(array('max' => 150))
            )
        ])
        ... many other attributes with choices or not

I thought of a checkbox that grey out the fields of the parent 2 if unchecked, but the member values are all required. Because of that SF2 does not validate my form.

If I set required => false to these fields (in the builder) then the user will have the possibility to validate without filling everything (which I don't want).

I'd like to create the following process :

  • Either we fill all the fields of the member2 in order to validate the form
  • Either we check a checkbox (single parent) and no field is required, and my final member2 will be null (or another solution)

1 Answer 1

13

After reading a lot of documentation, I found the solution of my problem here : http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data

In order to make an entity not required, you ought to add events listener and set the data as null post submit.

First step

Add the orphanRemoval=true option to your attribute

/**
 * @ORM\OneToOne(targetEntity="AppBundle\Entity\Member", orphanRemoval=true, cascade={"persist", "remove"})
 * @ORM\JoinColumn(name="parent2_id", referencedColumnName="id",nullable=true)
 */
private $parent2;

Second step

Add a new field to your form, a not mapped checkbox

   $builder
        ->add('parent1', MemberType::class)
        ->add('withParent2', CheckboxType::class, [
            'mapped'            => false,
            'required'          => false,
            'data'              => true
        ])
        ->add('parent2', MemberType::class, [
            'required'          => false
        ])

We'll use this checkbox to set the parent2 to null if not checked.

Next to this, add your event listeners :

   //this event will set whether or not the checkbox should be checked
   $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $form = $event->getForm();
        $family = $event->getData();

        if ($family->getId()) {
            $form->add('withParent2', CheckboxType::class, [
                'mapped'        => false,
                'required'      => false,
                'data'          => $family->getParent2() ? true : false
            ]);
        }
    });

    //Event when the form is submitted, before database update
    $builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {

        //if the checkbox was not checked, it means that there was not a second parent
        $withParent2 = $event->getForm()->get('withParent2')->getData();
        if (!$withParent2) {

            // we set this attribute to null, and disable the form validation
            $event->getData()->setParent2(null);
            $event->stopPropagation();
        }

    }, 900);

Third step

Our form is working fine this way, the only problem left is the javascript verification.

Just do a jquery function that remove the required attribute from your fields.

 function toggleParent2Requirement(checked){
        if (!checked) {
            $("[id^=family_parent2]").prop("required", false);
            $("[id^=family_parent2]").attr('disabled', true);
        }
        else {
            $("[id^=family_parent2]").prop("required", true);
            $("[id^=family_parent2]").attr('disabled', false);
        }
    }

Here you make a oneToOne relation optional. The only part that I'm not proud is the stopPropagation part. This was in the documentation, and I don't know if we can only disable this field's verification in a more clean way.

Sign up to request clarification or add additional context in comments.

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.