Problem/Motivation
In my base theme I run a lot of extra features as theme settings. I validate and submit these using custom functions and in Drupal 7 I could add these to the form array, usually the submit button - since validate and submit are just boring old arrays.
In Drupal 8 - when I do this the core submit and validation functions do not fire - only my new validation and submit functions fire (the ones I explicitly declare in the form alter). This is a regression.
For example when I do the following, none of the basic toggle settings will save - because afaict Drupal cores own submit/validation fails to fire, its like #submit and #validate are not arrays to begin with and I am wholesale replacing them in the alter - but who can tell, since you cannot see these in the form array at all, in fact it's like they don't exist at all and I am adding something new to the form entirely:
$form['actions']['submit']['#validate'][] = 'mytheme_validate_advanced_settings';
$form['actions']['submit']['#submit'][] = 'mytheme_submit_advanced_settings';
So:
1) Either I am doing something wrong here (yay, easy fix), or
2) Major bug and a regression?
I am posting this as a bug because I asked in IRC a few weeks ago and was confirmed this should still work like D7, but it does not afaict.
Proposed resolution
Remaining tasks
User interface changes
API changes
Comment | File | Size | Author |
---|---|---|---|
#18 | submitForm-2252165-18.patch | 9.56 KB | tim.plunkett |
#18 | interdiff.txt | 654 bytes | tim.plunkett |
#3 | starkers.zip | 20.39 KB | Jeff Burnz |
Comments
Comment #1
tim.plunkettThis should work fine.
Can you provide some working example code to help reproduce?
Comment #2
er.pushpinderrana CreditAttribution: er.pushpinderrana commentedI have also tested this by alter core theme i.e seven and it is working fine for me. As tim.plunkett said, share some code with us, that would help to understand the exact scenario of this.
Comment #3
Jeff Burnz CreditAttribution: Jeff Burnz commentedI made a test theme and recorded a video showing what I am doing etc. The focus here is in the toggle display settings and what happens with those. I'd be incredibly grateful to find out what is going wrong here, so thank-you for taking a look.
http://screencast-o-matic.com/watch/c2fUQhnwiI
Comment #4
tim.plunkettOkay I can reproduce. I'll work on writing a test for this.
There are two problems here.
First, you should be using
$form['#submit'][] = 'starkers_submit_settings';
Using the button level #submit explicitly skips the generic form submission handler, and that is true in D7 as well.
But, using the top-level #submit is broken just for the theme-specific alters in theme-settings.php, because it runs *before* the regular form builder, not alongside the other alters.
Comment #5
tim.plunkettOkay, this wasn't too bad.
If you add $form['#submit'] or $form['#validate'] in your build form (which the theme-settings.php tacks onto), it currently suppresses the regular form handlers.
I don't think this is desired.
You can still suppress them by having button level submit handlers instead.
Comment #7
star-szrMakes sense.
Comment #8
sunHm. This has been a known/desired behavior and design choice of Form API in the past. We should look up and study why.
Can we re-implement the test as either DUTB or perhaps even phpunit, please? The functionality that is being changed here is not related to a theme at all — it's bare metal
FormBuilder
processing.Comment #9
sunOne related issue, and #38349 is the original Form API patch that introduced the #validate/#submit properties.
Comment #10
sunFrom #38349-6: new workflow for form validation/execution:
It is true that we're performing such overrides via button-level handlers in the meantime. However, there's one major pitfall:
The node entity form force-executes the form-level handlers.
Or perhaps even all entity forms in D8? Or has that code been refactored/removed in D8? — In any case, we need to ensure that the claim of "use button-level handlers to override everything" is actually valid and works in all cases. Otherwise, this change would result in non-overridable handlers for (some) forms.
Marking needs work at least for the test — can't see why that can't be a phpunit test (method).
Comment #11
tim.plunkettYou're misunderstanding the issue.
This has nothing to do with entity forms, or even really form builder processing itself.
It is specifically about ThemeSettingsForm.
Please read ThemeSettingsForm::buildForm, at the bottom.
Yes, the fix included might be a bit heavy handed, but phpunit is not going to be able to test this flow properly (require_once and function_exists).
Comment #12
sunForgot to mention that I do support this API change.
I also agree that theme settings form needs better test coverage + ideally some redesign. However, for this particular issue + change, the involvement of a theme just happens to be part of the symptom, but that is irrelevant for the actual fix/change. We really need to become strict(er) on that, because that's how HEAD has ended up with lots of duplicate/nonsense test coverage (in ugh, webtests).
Attached patch re-implements the test as DUTB (since @tim.plunkett was skeptical whether phpunit would work).
I didn't touch the functional change; only applied it after writing the DUTB test.
Comment #14
tim.plunkettRemoving my test coverage is pretty rude of you.
I understand your disdain for webtests, but I wrote it because that's how this issue was discovered, and ThemeSettingsForm is VERY different from how other forms work.
Please restore the test you removed.
Comment #15
mermentau CreditAttribution: mermentau commentedThe patch in #12 worked for me on MAYO 8.x-1.x-dev. It's on a core alpha-11 install. Didn't test extensively, but was sure happy to find the patch.
Comment #16
tim.plunkettThis is the RTBC patch from before, but with sun's added tests, which I've reviewed.
Comment #18
tim.plunkett#2228763: Create a comment-type config entity and use that as comment bundles, require selection in field settings form added a workaround for this issue, and alongside this fix, it causes the submit handlers to run twice.
Removing that.
Comment #19
sunSorry for removing those other changes - they felt off-topic for this issue.
Adjusting issue title, as this pertains to form-level handlers, not button-level handlers. The behavior of button-level handlers should not be affected by this change. (Or did I miss something?)
This will need a change notice (although I think that only a few modules will be affected).
Comment #20
alexpottI can't see why we need a change notice here - but if we do one needs to exist before a commit.
Comment #21
sunWhile not really documented, it is a known fact that a custom #validate and/or #submit handler causes the "default" handlers to no longer get assigned automatically.
Before this change, you had to manually specify the default #validate/#submit handler(s) in case your form constructor defined (additional) custom handlers.
After this change, you need to remove any manually specified default handlers, because they would be invoked twice otherwise - as exemplified by #18.
A duplicate #validate handler is most often (but not always) a big deal, but a duplicate #submit handler breaks things badly.
This only applies to form constructors that specify additional handlers on top of the default handlers.
Lastly, this change implies that a form constructor is no longer able to omit the default handlers by explicitly setting #validate/#submit handlers to different values. (However, a form alter hook and everything else running after
prepareForm()
is still able to do so.)Comment #22
catchWhile it's a bit obscure, I think #21 is worth putting in a change notice.
Comment #23
sunSleeping over this...
The proposed API change here represents ~50% of the following older issue, which proposes to swap the invocation order of form construction functions under the hood:
#1452922: Run drupal_prepare_form() before the form constructor instead of after
If the form constructor gets a fully prepared
$form
injected already, then most of the magically determined callbacks can be introspected and manipulated in a more controlled way by your code:You can
var_dump($form)
and you'll see that#submit
will invoke e.g.$this::submitForm()
. In turn, you know whether you want to$form['#submit'][]
orarray_unshift($form['#submit'], ...)
.The same applies to
#validate
,#theme
,#attributes
, and everything else that is being set up inFormBuilder::prepareForm()
.Comment #24
mermentau CreditAttribution: mermentau commentedI don't see any commits involved with this issue and yet I don't see the problem after Pull of today's Drupal 8 from git.
Comment #25
tim.plunkettDuplicate of #2268761: Remove support for function-based forms
Comment #26
maijs CreditAttribution: maijs commentedThis issue also persists in configuration forms of Drupal 8 core modules which extend
ConfigFormBase
class. At this point even Drupal 8 core modules do not play well together as described in #2355053: ConfigFormBase::buildForm() is missing a default submit handler.This means that there's no way to gracefully add additional submit handlers to Drupal 8 configuration forms because any
$form['actions']['#submit'][] = 'MYMODULE_form_submit';
code will disableConfigFormBase::submitForm()
method execution.Comment #27
BerdirThat is by design. If #submit handlers are set on a specific submit element, then they override the default.
Use the top-level $form['#submit'][] to add additional and still have the default implementation called.
Comment #28
UnsettlingTrend@Berdir, sorry, I hate adding, effectively, a "+1" post, but this saved me. Thanks!