Problem/Motivation
The FormBuilder class is complex and long, but large portions of it are not actually for building and processing, but for validation.
This makes it harder to unit test, harder to understand the code flow, and harder to maintain.
Proposed resolution
Move form validation into a new class.
In order to minimize the changes for existing module code, have FormBuilder pass through those method calls to the new class.
Remaining tasks
N/A
User interface changes
N/A
API changes
flattenOptions() is now a static method on a new \Drupal\Core\Form\OptGroup class.
executeHandlers(), which took either 'submit' or 'validate' as the first parameter, is now split into executeSubmitHandlers() and executeValidateHandlers().
These are not called anywhere externally, since they are still wrapped by the deprecated form_execute_handlers(), which handles this change.
Original report by @shrikeh
Trying to give the rather bulky FormBuilder a bit more separation of concerns, I notice that currently the FormBuilder validateForm and doValidateForm could be moved into a separate form validation class. It also has the advantage that currently the only translation referenced in FormBuilder is during validation.
| Comment | File | Size | Author |
|---|---|---|---|
| #9 | form-2209977-9.patch | 104.49 KB | tim.plunkett |
Comments
Comment #1
tim.plunkettHere's how I envisioned this.
I was also able to add more comprehensive unit tests, which is a major plus.
Comment #2
tim.plunkettRemoved left over code I meant to refactor out.
Comment #4
tim.plunkettMore test coverage.
Comment #5
tim.plunkettSplitting executeHandlers() into two methods, and moving flattenOptions into a static helper class are the only API changes here.
For BC purposes, FormBuilder still implements all of the original methods, and just passes the calls through to the new FormValidator class.
A side benefit is that FormBuilder no longer cares about string translation, drupal_set_message, or watchdog.
$formswas leftover, and$flattenedOptionsis now part of FormHelper, so now all of the properties of FormBuilder are other services, no more state!Comment #6
dawehnerNice refactoring!!
Could we just go with FormUtility or something better?
The injection seems wrong. We should also use the stack
Let's really use the stack.
Comment #7
tim.plunkettRerolled this on top of #2225605: Use request stack in form builder, it now includes that patch.
Comment #8
tim.plunkettPer a discussion on IRC with sun, opened #2257835: Move form submission logic out of FormBuilder into a new class to do the same for submit.
Also when rewriting the services, I mistakenly did not inject string translation. Thanks @dawehner and @sun for catching that.
Fixed that and removed the optional-ness of CSRF generator.
Comment #9
tim.plunkettRerolled, that's in.
Comment #10
sunThe only remaining remark I had/have is whether we can rename the
FormValidator::executeValidateHandlers()method intoFormValidator::executeHandlers()?The repetition of "validate" is unnecessarily verbose on a separate class/object. Especially given the follow-up issue, having a consistent
executeHandlers()method on both theFormValidatorInterfaceand theFormSubmitterInterfacewould be logical.This means that
FormBuildercannot implementFormValidatorInterface, but I'm interpreting that just as a temporary legacy/BC layer anyway, so we can easily resolve that by leaving the current methods onFormBuilderInterface+ just replace their phpDoc to state that they are @deprecated proxies for the correspondingFormValidatormethods.Aside from that, I really like the idea of exposing the main 3 concepts of Form API as separate classes (build/validate/submit). That will certainly help many developers to understand the fundamental architectural design of our form processing.
Comment #11
tim.plunkettI think I'd like to hold off on renaming that until #2257835: Move form submission logic out of FormBuilder into a new class, because I'm honestly not as confident about that issue as this one. And if that one doesn't pan out, changing executeValidateHandlers to executeHandlers will make much less sense.
Comment #12
sunHm. That would mean a second API change though...
I think it would be weird/confusing to only split out validation. I'm marking this RTBC on the assumption that we'll also split out submission.
Yes, that's less much less code and it might be harder (because some parts are intermixed into [re]building) — however, we wouldn't have to move the input processing, just the
executeHandlers()+submitForm()logic that's contained inprocessForm()... at least that's what I thought of regarding parity for clarity.Comment #13
alexpottLet's get a followup to move OptGroup tests out of FormBuilderTest.
Plus before this can be done we need a change notice or a list of change notices that need updating.
Comment #14
alexpottCommitted f710a6c and pushed to 8.x. Thanks!
Let's update the change notices https://drupal.org/node/2121003 and https://drupal.org/node/2241767
Comment #16
alexpottCreated #2259301: Move OptGroup tests out of FormBuilderTest
Comment #17
tim.plunkettAdded https://drupal.org/node/2259297 as a change notice for OptGroup, and updated those two.
Thanks, see you in #2257835: Move form submission logic out of FormBuilder into a new class
Comment #18
tim.plunkettComment #19
jibranI think @deprecated can be added here.