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 :
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.
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;
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);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With