Problem/Motivation

The form system is currently closely coupled to the Module and Theme system via alters. That's undesireable as it makes it harder to test the core of the form system. It also inhibits our ability to (later), abstract FAPI out entirely. (Whether there is value to doing so or not, there are architectural benefits to the decoupling that would entail.)

Proposed resolution

Factor the module and theme dependencies out of FormBuilder and into an interstitial "alter" object. That adds a decoupling between FormBuilder and Module/ThemeHandler. The net effect is identical for the 99% use case, but improves the separation of concerns within FAPI.

This is primarily an internal improvement to FAPI and not an API change for anyone except core FAPI developers.

Remaining tasks

Commit?

User interface changes

None.

API changes

No public facing changes; just internal to FAPI.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tim.plunkett’s picture

Status: Active » Needs review
FileSize
9.99 KB

Status: Needs review » Needs work

The last submitted patch, 1: form-2328871-1.patch, failed testing.

dawehner’s picture

  1. +++ b/core/lib/Drupal/Core/Form/FormAlterSubscriber.php
    @@ -0,0 +1,66 @@
    +    $events[FormEvents::ALTER][] = 'onFormAlter';
    

    <3 for proper constant usage!

  2. +++ b/core/lib/Drupal/Core/Form/FormEvents.php
    @@ -0,0 +1,17 @@
    +final class FormEvents {
    +
    +  const ALTER = 'form_builder.alter';
    +
    +}
    

    I still don't get why everyone wants to mark them as final.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
11.58 KB
4.67 KB

It's final because all of the others are... Discussed in IRC, no one really was concerned about this.

Fixed bugs, and expanded docs.

jibran’s picture

Status: Needs review » Reviewed & tested by the community

In reality I'd wait for #2328777: Refactor FAPI getCache()/setCache() into a standalone class but I know you'll handle reroll ;) So RTBC.

On the side note I was thinking what can I do with my own event subscriber. Can you come up with some ideas?

tim.plunkett’s picture

Issue summary: View changes
FileSize
12.86 KB

Rerolled for #2328777: Refactor FAPI getCache()/setCache() into a standalone class
Now moduleHandler is removed from FormBuilder!

webchick’s picture

Assigned: tim.plunkett » catch

So overall, I can see the idea behind this. And I like that we retain BC so modules don't actually have to change any code. But we get into murky territory here by converting a one-off hook (and not just some silly bootstrap hook that 8 modules implement, but one of *the* most oft-used hooks, esp. in custom modules) to an event and not doing so across the board. Because of the frequency with which this hook is used, it also means that if this patch goes in, contrib is likely to result in a "potpourri" of different approaches, where module A uses hook_form_alter, and module B uses the FormAlterEvent and it generally gets much harder as a developer to skip back and forth between them. (I realize this isn't the intent, but I believe would be the effect, and if we're going to go that route, I'd much prefer to give all hooks this option via something like #1972304: Add a HookEvent).

Anyway, of the core committers, I feel like catch has the strongest opinions about when to / not to use the Symfony event system in lieu of / alongside the Drupal hook system, so assigning to him after talking to Tim.

chx’s picture

It is not my role to agree or disagree with a D8 patch so as the D7 form maintainer: the ordering of hook_form_alter vis form_FORM_ID_alter is already a confusing mess.

tim.plunkett’s picture

The reality that other modules could subscribe to this event instead of using a hook is an unintended side-effect. @webchick and @chx are definitely right that it would add to the confusion.

One other suggestion by @Crell was to have a simple service that FormBuilder calls out to, and let it handle the moduleHandler/themeManager.
This isn't very urgent though, so I'll leave it for @catch first.

tim.plunkett’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
14.11 KB
13.3 KB

Here's a non-event-based solution. This one just encapsulates it in a private service.

Pros: Easier to test, no danger of modules abusing it
Cons: Swaps two dependencies for one instead of zero

Also comes with unit tests.

Status: Needs review » Needs work

The last submitted patch, 10: form-2328871-10.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
724 bytes
13.33 KB
jibran’s picture

I agree with @chx and @webchick but IMHO we are going to end up with event base solution anyway when we'll replace hooks with events. As @webchick said it is most used hook why not take this opportunity to encourage contrib developer to use events instead of hooks.

So +1 for event base solution.

tim.plunkett’s picture

Because we would likely implement it completely differently, with one event for hook_form_alter, one for hook_form_BASE_FORM_ID_alter and one for hook_form_FORM_ID_alter, to ensure they run in the correct order.
And also, themes cannot subscribe to events, and they need to be able to alter forms.

Crell’s picture

What is event_dispatcher used for other than alter right now?

One advantage of the FormAlter object abstraction is that it decouples form altering from *any* particular implementation. That means there's no dependency on hooks, OR on EventDispatcher (hence my question above for what else it's used for). That allows us to vary the implementation later if needs be, or per-site. (So if someone really wanted events for alters as well on their site for some reason, that would be possible.)

chx suggested a similar approach a while back to eliminate the hook_query_alter() call in the database layer. I think it's a good idea but we never really got around to it. We should do the same there, too, though.

dawehner’s picture

I wonder whether we considered to have a alterdecorator for forms instead of passing it along.

Crell’s picture

That would be possible, but would be somewhat more involved as we'd need to forward every method, not just the one we are throwing an alter on. I think in this case it's OK to make it an intrinsic part of the concept of form processing, just via a separate object.

tim.plunkett’s picture

Assigned: catch » Unassigned
FileSize
13.39 KB

Rerolled. I have no real opinion on the decorator idea.

Also, since we're not trying to use events anymore, this shouldn't be blocked on @catch.

Status: Needs review » Needs work

The last submitted patch, 18: 2328871-form_state-18.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
13.41 KB
635 bytes
znerol’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll
rpayanm’s picture

Status: Needs work » Needs review
FileSize
13.48 KB
Crell’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs reroll

This improves decoupling, has zero impact on module developers, and is a change only to a constructor signature. This very low impact, moderate benefit => IMO RTBCable.

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Need issue summary update

If you swap out the new service, and for example don't invoke hook_form_alter() at all, then all existing form_alters become dead code. That doesn't look viable for any real use case. So while this patch removes an explicit dependency on the module and theme system, it does nothing to remove the implicit dependency which is very much baked in.

If the goal is to use the form builder entirely outside of Drupal that also doesn't seem feasible given the current state of form API. If someone can make the case that we're actually quite close to this point and this patch really helps there, then that might help a bit.

On top of that, the issue summary is completely contrary to the patch.

Status: Needs work » Needs review

jibran queued 22: 2328871-22.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 22: 2328871-22.patch, failed testing.

Crell’s picture

Issue summary: View changes

Needs reroll now.

I updated the IS with a better description of what the end result is. This doesn't allow us to use FAPI outside of core itself; it nominally moves us closer but the bigger point is just better decoupling and separation of concerns with FAPI. It's better quality code and a small benefit to testability, which IMO is justification enough as there's no downside to module developers. (In theory one could swap out the alter service but I really don't expect anyone to do so; that is in no way an argument that it shouldn't be its own class, however, as it's more about separation of concerns than making such swappability a user-facing "feature".)

effulgentsia’s picture

Adding #2402445: Implement object oriented form alters as a related issue, since it also adds a FormAlterInterface, though an entirely different one, to solve a different problem.

rpayanm’s picture

Status: Needs work » Needs review
FileSize
13.38 KB

rerolled

Crell’s picture

Status: Needs review » Reviewed & tested by the community

Back up to the committers. (Will retest momentarily.)

Crell queued 29: 2328871-29.patch for re-testing.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 29: 2328871-29.patch, failed testing.

jclema’s picture

Issue tags: -Need issue summary update +Needs issue summary update
tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
15.06 KB
2.54 KB

Rerolled, with some changes.

Crell’s picture

Status: Needs review » Reviewed & tested by the community

Oh yeah, this issue. :-)

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 34: 2328871-form_alter-34.patch, failed testing.

rpayanm’s picture

Issue tags: +Needs reroll
bojanz’s picture

Assigned: Unassigned » bojanz

I'll reroll.

bojanz’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
15.04 KB

It was actually still applying, though with big offsets, so here's a fresh one.

Status: Needs review » Needs work

The last submitted patch, 39: 2328871-form_alter-39.patch, failed testing.

bojanz’s picture

Assigned: bojanz » Unassigned

The test fail is "Updates for Contrib module one" in Drupal\locale\Tests\LocaleUpdateTest->testUpdateImportSourceRemote().
Seems unrelated?

Status: Needs work » Needs review
bojanz’s picture

Status: Needs review » Reviewed & tested by the community

And we're back.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 39: 2328871-form_alter-39.patch, failed testing.

Status: Needs work » Needs review
Crell’s picture

Status: Needs review » Reviewed & tested by the community

Still back.

catch’s picture

Status: Reviewed & tested by the community » Needs review

The test change replaces two mocks with one mock, I'm still missing how the additional indirection is worth introducing for having to mock one less thing. This feels like 'service-per-method' rather than real decoupling still, and there are other issues untangling actual dependencies we could be spending the energy on instead.

tim.plunkett’s picture

Oh, I thought this went in. Whoops :(

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

phenaproxima’s picture

Version: 8.2.x-dev » 8.3.x-dev
FileSize
15.85 KB

At @tim.plunkett's encouragement, I have taken a shot at turning hook_form_alter into an event. This patch removes FormAlterInterface in favor of FormAlterEvent, but it keeps the existing FormAlter class doing exactly what it was already doing -- it is merely trigged using the normal event dispatcher now.

I didn't update the tests (or comment anything, really), so it will probably definitely fail horribly. This is more a proof of concept at the moment, to test the waters and see if people want to pursue this approach further.

I apologize for the lack of an interdiff; some hunks of the original patch didn't apply against 8.3.x.

Status: Needs review » Needs work

The last submitted patch, 51: 2328871-51.patch, failed testing.

phenaproxima’s picture

Status: Needs work » Needs review
FileSize
16.93 KB
4.95 KB

This oughta fix the tests.

Status: Needs review » Needs work

The last submitted patch, 53: 2328871-53.patch, failed testing.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
16.92 KB

Rerolled.

Status: Needs review » Needs work

The last submitted patch, 57: 2328871-form_hook-57.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
1.28 KB
16.82 KB

Private services are incompatible with ContainerAwareEventDispatcher. Also missed a reference.

Status: Needs review » Needs work

The last submitted patch, 59: 2328871-form_alter-59.patch, failed testing. View results

catch’s picture

#7, #9, #14, #24 and #47 on this issue (possibly others) haven't been responded to adequately yet to justify why we'd do this.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.