Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
PluginFormInterface
allows plugins to build their own forms. It accepts form elements and the form state.
The problem is that the interface does not clearly document the form elements are the plugin's own elements, and not the parent form the plugin form may be embedded in. The reason for this is that otherwise the plugin has no way to find its position within the complete form, or the form state's submitted values. This causes problems like #2532968: Block plugin forms assume they are top-level.
Proposed resolution
Document that the $form
parameter accepts the plugin's own form elements.
Remaining tasks
None.
User interface changes
None.
API changes
None.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#21 | interdiff.txt | 1.61 KB | tim.plunkett |
#21 | drupal_2532970_21.patch | 3.54 KB | tim.plunkett |
Comments
Comment #1
XanoComment #2
jhodgdonThis change seems OK but I'm not convinced it solves the problem stated in the issue summary. Maybe in addition to calling it "the plugin form", you could add a few words to indicate it is possibly just a small piece of a larger form?
Actually though, why would that be the case?
And it seems to me that if $form_state->getValue* doesn't work, that is a larger problem than just plugin forms. I'm sure we have this assumption all over core for form builders. None of them will work if the form is embedded elsewhere, so it sounds like no form should make this assumption?
Comment #3
XanoFormInterface
exists for complete forms.PluginFormInterface
–which does not have a form ID and therefore cannot be a form of its own–exists so plugins can provide embeddable form segments. It is technically impossible to use these as forms on their own; they must always be embedded in an actual form.FormStateInterface::getValue()
is fine to use, as long as you know where exactly in the values you need to look. Embeddable forms can never know, so they must use#array_parents
and#parents
to determine their place in the form and form state. This is known and designed behavior, and it's already partly documented inPluginFormInterface
's docblock.Comment #4
jhodgdonOK, fair enough.
So in order to really document this well, so that this type of problem will not happen in the future, what I would suggest is:
a) Put something into the FormStateInterface methods where you need to pass in the parents that says something like:
If you are calling this from an embeddable form (a form meant to be embedded in a larger form), you must pass in (whatever the parents parameter is called).
b) In PluginFormInterface in the class docs header, document that plugin forms are meant to be embedded and that you need to be careful about assumptions when you get values out of the $form_state.
c) I still think that the patch you've submitted here doesn't do enough for those methods either... My suggestion would be instead of just changing the docs on those methods for the $form argument from saying "form" to "plugin form", let's say "embeddable plugin form", and then on $form_state, let's say "for the entire form (including the embedded plugin form)" or something like that to get across the point that $form is just the plugin portion, and $form_state is the entire form state.
Does that make sense?
Comment #5
XanoFormStateInterface
does not support getting nested values. Besides, it's not the form state, but the form itself that is different, so documentation should remain there.This was already documented, but may have been unclear because the documentation said plugin forms were usually embedded. The patch changes the wording so it's clear they are always embedded.
Comment #7
XanoThat looks like a rebase error.
Comment #8
XanoNow with code suggestions.
Comment #9
jhodgdonThanks for the new patches! I think this still needs some work though:
a embeddable -> an embeddable
Also this one line doc just seems wrong. As it reads now, it says it is an interface for a plugin, but I think it is actually an interface for a plugin *form* right? Or maybe it's a plugin configuration form really? I think that is what it actually is.
So the first line needs a rewrite.
A code example would be helpful here, or it could go into the buildConfigurationForm() method. Actually, most of this documentation is specific to buildConfigurationForm() so maybe it should go there instead?
Maybe in this method, make sure that again it is stressed that #array_parents and #parents are not set in $form?
Why would it be so terrible on the $form_state docs here to add the word "entire" so that it is clearer that $form_state and $form are not referring to the same thing?
Um, is it really buildForm() or buildConfigurationForm()?
Again, could we say "state of the entire form" here?
Any way to split this line to less than 80 characters? Also probably should use @code ... @endcode since this is not just one function name but a code example.
again, is this really from buildForm() or buildConfigurationForm()?
Again, could we say "entire form" here?
Again, use @code / @endcode
Comment #10
jhodgdonAlso, just as a note, our coding standards for docs say that when referring to a class/method in docs, you put in the entire namespace and class name (or in this case, interface name) rather than using shortcuts like static::methodName().
Comment #11
XanoThanks for the extensive review! I will roll a new patch.
self
would be a shortcut, but static definitely isn't (see late static bindings). Besides that, I am all in favor of being explicit, but usingstatic
orself
here has the added advantage that we don't have to change the code example whenever the class is renamed or moved. Even if this does indeed violate our current standards, I'd argue that the standards should be relaxed as these references do not point to something external.Comment #12
XanoThat's a good idea. Thank you. I did opt for complete rather than entire, because that's what the form array contained by form states are called.
Comment #13
XanoAs we discussed on IRC I did not bluntly ignore the feedback about
static
versus FQNs. I created #2536670: Relax class/interface reference documentation standards to explicitly allow these forms of documentation.Comment #14
jhodgdonI like it! Just a couple of nitpicks and one substantive question/comment...
nitpick: this crosses 80 characters.
If this is staying here (see next comment), maybe say "this method" instead of "buildConfigurationForm()" ?
Well, the problem with moving this whole block of docs to the buildConfigurationForm() method doc block is that this part is not related to this method, so it doesn't belong on this method doc... not sure what to do? I guess on the validate/submit methods, you could say something like "Unlike on the build method, ..." or something?
over 80 chars, rewrap
80 chars
Comment #15
XanoComment #16
jhodgdonNow, this looks good and I think it accomplishes the goals of this issue: getting across to the developer that they need to be careful with this stuff. !! Great!
Comment #17
tim.plunkettPlease leave this out until #2536646: [meta] PluginFormInterface implementations assume they are top-level and friends are decided upon.
Also putting this into documentation prevents the subsystem maintainers from easily finding their issues.
Comment #18
tim.plunkettComment #19
jhodgdonSorry, didn't realize it was controversial or I definitely would have put it into that component.
Comment #20
Xano@tim.plunkett: if you feel this needs more consideration, couls you update either issue with the reason why?
Comment #21
tim.plunkettI meant this.
Comment #22
jhodgdonThis seems acceptable to me. Provisionally setting to RTBC unless Xano objects.
Comment #23
XanoI'm okay with it. Let's tackle the form state issue in #2536646: [meta] PluginFormInterface implementations assume they are top-level.
Comment #24
alexpottDocs are not frozen in beta. Committed b116b58 and pushed to 8.0.x. Thanks!