There are many many questions here on SO about how to properly persist checkbox fields in Laravel after form submission ( example, example, example ), this question is not about that.
Please note I am also well aware of Laravel's old()
method, and that it accepts a default value. My question is about a particular case where old()
does not seem to work for a checkbox.
I'm working on an edit form, updating data already in the DB. The form will initially be populated with the values from the DB.
The standard approach to re-populating form inputs after failed validation is to use old()
. For most input types, something like the following works:
<input type='text' name='unicorns' value='{{ old('unicorns', $model->unicorns) }}'>
On initial page load (before the form is submitted) this works fine. As nothing has been submitted yet, old('unicorns')
is null
, so the default of $model->unicorns
will be used, and the text field's value reflects the current DB state.
After submitting a new value, which fails validation, old('unicorns')
will be the new value, even if the new value was an empty string, and again the text field's value reflects the submitted state, as we want.
However this breaks down for checkboxes, when the unchecked checkbox does not appear in the request at all. Consider:
<input type='checkbox' name='unicorns' value='1' @if old('unicorns', $model->unicorns) checked @endif>
On initial page load this works fine, just like for text fields. But after failed validation, for the 2 cases where the checkbox is changed from its initial state:
Case 1: DB state
0
(unchecked), submitted state checked.old('unicorns')
will be1
, so the test evaluatestrue
, the checkbox is checked - OK!Case 2: DB state
1
(checked), submitted state unchecked. Sinceold('unicorns')
is null,old()
falls back to the default value$model->unicorns
(1
), so the test evaluatestrue
, and the checkbox is checked - but we just unchecked it! FAIL!
(There is no problem in the other 2 cases, where the checkbox is not changed from the state in the DB).
How to solve this?
I initially thought the best way around this would be to test if there has been a validation error - if so, use the old()
value, with no default fallback; if not, use old()
with default fallback. One way to test for failed validation is to check $errors
. This seems to work, but it only works within a view AFAICT, as (from the Laravel docs):
Note: The $errors variable is bound to the view ...
I'd like to create a global helper for this, and $errors
won't work there. Anyway, this approach feels ... clunky and too complicated.
Am I missing something? Is there a neat Laravel way of solving this? It seems odd that old()
simply does not work for this particular case.
This is not a new problem, how do others handle this?