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

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tim.plunkett’s picture

This should work fine.
Can you provide some working example code to help reproduce?

er.pushpinderrana’s picture

I 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.

Jeff Burnz’s picture

FileSize
20.39 KB

I 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

tim.plunkett’s picture

Title: Cannot add validate or submit functions to theme settings form » Theme-specific validate or submit functions in theme-settings.php will prevent normal form processing
Assigned: Unassigned » tim.plunkett
Issue tags: +Needs tests

Okay 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.

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned
Status: Active » Needs review
Issue tags: -Needs tests
FileSize
2.8 KB
6.17 KB

Okay, 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.

The last submitted patch, 5: theme-settings-2252165-5-FAIL.patch, failed testing.

star-szr’s picture

Status: Needs review » Reviewed & tested by the community

Makes sense.

sun’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +API change

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.

Hm. 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.

sun’s picture

One related issue, and #38349 is the original Form API patch that introduced the #validate/#submit properties.

sun’s picture

Status: Needs review » Needs work

From #38349-6: new workflow for form validation/execution:

a module could simply add an element to the #validate array via hook_form_alter if it wished to perform additional validation, or it could redeclare the entire #validate array if it wanted to override any existing validation

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).

tim.plunkett’s picture

Status: Needs work » Needs review

The functionality that is being changed here is not related to a theme at all — it's bare metal FormBuilder processing.

You'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).

sun’s picture

Forgot 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.

The last submitted patch, 12: form.handlers.12.fail_.patch, failed testing.

tim.plunkett’s picture

Status: Needs review » Needs work

Removing 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.

mermentau’s picture

The 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.

tim.plunkett’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
8.92 KB

This is the RTBC patch from before, but with sun's added tests, which I've reviewed.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 16: theme-settings-2252165-16.patch, failed testing.

tim.plunkett’s picture

Title: Theme-specific validate or submit functions in theme-settings.php will prevent normal form processing » submitForm does not run when custom submit handler is set on the submit button
Status: Needs work » Needs review
FileSize
654 bytes
9.56 KB

#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.

sun’s picture

Title: submitForm does not run when custom submit handler is set on the submit button » submitForm() is not called when custom form #submit handler is set
Status: Needs review » Reviewed & tested by the community

Sorry 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).

alexpott’s picture

I can't see why we need a change notice here - but if we do one needs to exist before a commit.

sun’s picture

While 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.)

catch’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs change record

While it's a bit obscure, I think #21 is worth putting in a change notice.

sun’s picture

Sleeping 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'][] or array_unshift($form['#submit'], ...).

The same applies to #validate, #theme, #attributes, and everything else that is being set up in FormBuilder::prepareForm().

mermentau’s picture

I 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.

tim.plunkett’s picture

Status: Needs review » Closed (duplicate)
maijs’s picture

Status: Closed (duplicate) » Active

This 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 disable ConfigFormBase::submitForm() method execution.

Berdir’s picture

Status: Active » Closed (duplicate)

That 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.

UnsettlingTrend’s picture

@Berdir, sorry, I hate adding, effectively, a "+1" post, but this saved me. Thanks!