I have a big entity and a big form. When updating my entity, I only render parts of my form, through ajax calls. On client side, I'm using Jquery and html5 FormData, so I can also send files within my form. To make sure the fields that are not rendered won't be set to null in the process, I'm using PATCH method.
So when a field is not present in the request, it's left as is by Symfony.
But when the field I update is a boolean (rendered a a checkbox) that was set to true and I want to set it to false, it's not passed in the request, so my update is ignored.
Is there an easy way to force unchecked checkboxes to appear in the request?
EDIT
I found a way to force unchecked checkboxes to appear in the request, thanks to Felix Kling's comment on this question :
$("input:checkbox:not(:checked)").each(function() {
formData.set($(this).attr('name'), formData.has($(this).attr('id')) ? '1' : '0');
});
Unfortunately, this didn't solve my problem, because of Symfony's behaviour:
- When using PUT, if the boolean field appears in the request, it's set to true, regardless of its value (even if it's "0" or "false").
- When using PATCH method, the fields not appearing in the request are ignored.
Could that be solved with DataTransformer? (I've never used it)
You are absolutely right, Symfony will ignore it if method is PATCH because of this line in Request Handler:
$form->submit($data, 'PATCH' !== $method);
Now, I would generally suggest that you use a PUT
request if that is an option, but if it isn't then second argument to FormInterface::submit($submittedData, $clearMissing = true)
is what you're after.
The "proper" way would probably be to make your own implementation of Symfony\Component\Form\RequestHandlerInterface
which would force $clearMissing
to be true
.
Other, way is a lot easier but might not work for all use-cases: use $form->submit()
directly.
If you have the following code:
$form->handleRequest($request);
You can do:
$form->submit($request->get($form->getName()), true);
You can also omit second parameter since true
is the default value
Here goes a working solution, that could be improved.
To force unchecked checkboxes to appear in the request, thanks to Felix Kling's comment on this question, I've added this js before my ajax request :
$("input:checkbox:not(:checked)").each(function() {
formData.set($(this).attr('name'), formData.has($(this).attr('id')) ? '1' : '0');
});
Then, on the Symfony side, I had to override the BooleanToStringTransformer behaviour, that returns true
for whatever string and false
only for null value. Making a change in the last line, we now return false if the value doesn't match the value defined for true ("1" by default). So if the value returned by the form is "0", we get false, as expected.
public function reverseTransform($value)
{
if (null === $value) {
return false;
}
if (!is_string($value)) {
throw new TransformationFailedException('Expected a string.');
}
return ($this->trueValue === $value); // initially: "return true;"
}
Following the docs, I made my own DataTransformer, as well as a custom AjaxCheckboxType
Unfortunately, it seems that Symfony uses both DataTransformers (mine and the original one), one after the other, so it didn't work. In the docs they extend TextType
not CheckboxType
, that must explain the problems I encountered.
I ended up copying and pasting the whole CheckboxType class
in my own AjaxCheckboxType, only changing the DataTransformer's call in order to use mine.
A much nicer solution would be to totally override the DataTransformer, but I don't know how.
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