Active
Project:
Drupal core
Version:
main
Component:
forms system
Priority:
Normal
Category:
Bug report
Assigned:
Unassigned
Reporter:
Created:
25 Jul 2012 at 07:48 UTC
Updated:
7 Jan 2022 at 16:36 UTC
Jump to comment: Most recent
Split from #811542-247: Regression: Required radios throw illegal choice error when none selected since it wasn't working for older versions of Drupal 7
I was able to break it with tableselect, apply attached patch after applying #244, enable the form_test module, navigate to /form_test/tableselect/multiple-false and press submit: "An illegal choice has been detected. Please contact the site administrator."
The following code will throw "An illegal choice has been detected. Please contact the site administrator." after submitting
$form['tableselect'] += array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#multiple' => FALSE,
'#required' => TRUE,
'#empty' => t('Empty text.'),
);
Attaching the patches made @effulgentsia
| Comment | File | Size | Author |
|---|---|---|---|
| i811542-failing-D7-do-not-test.patch | 1010 bytes | attiks | |
| i811542-failing-D8-do-not-test.patch | 1.13 KB | attiks |
Comments
Comment #1
polGot the same problem in a form, I'm still looking for a solution.
Comment #2
bogdan.racz commentedCan you please provide the steps to reproduce it?
Comment #3
polHey, I was about to reply to this thread from home tonight.
Actually I fixed it, I was using the wrong array, see line 165.
Sorry for the noise.
Comment #4
cilefen commentedComment #5
alfaguru commentedI don't believe this is fixed. (Drupal 8.6.17)
In the function performRequiredValidation() of \Drupal\Core\Form\FormValidator, the validation of an element with options does not allow for the case of a non-multiple (ie not array) value for anything other than a select type.
For a required non-multiple tableselect, if nothing is selected, the code _always_ reports 'illegal choice' rather than field being required, via the condition at line 366.
The code linked at https://www.drupal.org/project/drupal/issues/1699644#comment-10608676 does not apply as #multiple is TRUE in that case.
The performRequiredValidation() function is not well factored. In an ideal world this sort of thing would be handled by the element class rather than a generic function, because it makes addressing this issue harder than it need be and I have not had time to find either a patch or workaround. Neither the form or element validation is called ahead of this code else I'd use that to work around the issue.
The form I am working on is for an admin tool so it's less critical otherwise I'd be willing to investigate further.
Comment #6
alfaguru commentedComment #9
cosmicdreams commentedFound this issue today because I have created a form that has a tableselect element that I load up with custom data.
I then have both a submit button and an additional button I use to gather more data. When I click that second, non-default button I always run into the "An illegal choice has been detected" error.
After following the logic some it looks like my element is initializing it's #value to be and array that looks like [0 => ""], that causes the logic in FormValidator::performRequiredValidation trigger an error on line 348 where it reads
Perhaps the problem is how this value is initialized. If there's no value to set, then the element wouldn't get the '#needs_validation' key and then the performRequiredValidation logic would not be triggered. I'm going to explore the reasoning why tableselect is getting a value to see if there is a way to avoid this trap.
Comment #10
cosmicdreams commentedThe problem could be Tableselect's valueCallback, which reads:
The key line is
return is_array($input) ? array_combine($input, $input) : [];. In this case $input = null. and therefore what is returned is an empty array. This leads the tableselect element to include the needs_validation key and that then triggers the performRequiredValidation logic that leads to our error.I'm searching for history around this callback to understand why valueCallback's empty case must always be an empty array. I think we're finding edge cases where having the value be an empty array leads to problems.
Comment #11
cosmicdreams commentedThe trail runs cold around 2014 where on 8/28 the tableselect was converted to an object for Drupal 8. From that time on (and likely before), the element has always returned an empty array and therefore may have triggered the needs_validation key when a button that isn't the submit button is submitted.
Next Steps:
* Explore if it is worthwhile to override the TableSelect element so I can use my own valueCallback, or do that in some way.
* Does setting a default value sidestep the issue? Doesn't seem like it should but I haven't tried that yet.
* What do other valueCallbacks do?
Comment #12
cosmicdreams commentedI haven't found a fix but I have found a workaround.
If you are creating a Form object, in your
buildForm(array $form, FormStateInterface $form_state): add a$form_state->setProgrammed();This establishes that your form isn't a default form, it was programmed by you. I believe this value is explicitly set when you invoke the submitForm method but when you programmatically include a tableselect render element you need it when the form rebuilds too.
There's likely a better way to do this, but I thought I'd share my "fix" if this sparks an idea in someone else. Maybe you can find the full fix.
NOTE:
If your form intends to kick off a batch process you'll need to set the form to "not programmed"
$form_state->setProgrammed(FALSE);if you want to see the batch process ui.