Problem/Motivation
Follow-up from #196972: How does hook_views_tabs() work?.
From #2614250-14: Number widget validation can break AJAX actions by amateescu:
\Drupal\Core\Field\WidgetBase::flagErrors(), line 423, $violation->getPropertyPath() returns an empty string, which means that $delta_element = $element; below will be $element[0]['value'] instead of just $element['value'], and that's why the call to $this->errorElement() below will send the wrong form array structure.
This probably causes the following major bugs:
- #2614250: Number widget validation can break AJAX actions
- #2553983: Required textarea with summary breaks ajax events for other fields.
- #2798115: Required Number field with a Paragraph field on Content Type causes AJAX Error
- #2714347: No way to set error on a field from an entity constraint validator
Proposed resolution
Let's add the $sub_property_path of a validation violation to the errorElement() method of widgets, so they can be easily used when mapping them to elements. Probably, we can also default to 1:1 mapping between violated properties and elements.
Right, now those sub property path is already calculated and available via the undocumented $violation->arrayPropertyPath
property.
Remaining tasks
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#87 | 2027059-nr-bot.txt | 132 bytes | needs-review-queue-bot |
#75 | 2027059-75.patch | 759 bytes | steveoliver |
#62 | drupal-2027059-62-test-only.patch | 1.87 KB | Dane Powell |
#62 | drupal-2027059-62.patch | 3.12 KB | Dane Powell |
Comments
Comment #1
fagoComment #2
fagoThis is getting handled as part from #2095195: Remove deprecated field_attach_form_*() now.
Comment #3
yched CreditAttribution: yched commented@fago: Is it ? I'm afk, bit iirc that other patch just paases ViolationList objects directly as a param to widget::flagErrors() instead of passing it through $form_state, but otherwise doesn't change anything regarding error paths ?
Comment #4
fagoTrue - looks like I confused that :/ Thanks, re-opening.
Comment #5
tim.plunkettThis is blocking #2509268: Inline errors repeated on child elements in module uninstall form, bumping.
Comment #6
tim.plunkettComment #7
bojanz CreditAttribution: bojanz at Centarro commentedWow, so $violation->arrayPropertyPath doesn't actually exist on the violation object, we just randomly assign it there in WidgetBase.
Didn't even know PHP allowed that.
Here's an initial patch. It stops passing $violation->arrayPropertyPath, and introduces a new method argument instead.
It also returns the correct error element.
No test work yet.
Comment #9
bojanz CreditAttribution: bojanz at Centarro commentedFriday afternoon intelligence. This time without the syntax error.
Comment #11
bojanz CreditAttribution: bojanz at Centarro commented$error and $violation were inconsistently used in the method signature, and I made it worse. Now fixed.
This one should be green, but we'll want to introduce additional test coverage.
Comment #12
yched CreditAttribution: yched commentedThen we should stop assigning $violation->arrayPropertyPath altogether, rather than assigning it and then unassigning it a couple lines below ?
Would mean building $violations_by_delta as
rather than
Comment #13
yched CreditAttribution: yched commentedBut more generally, it feels weird to have a method with params :
- $property_path (as an array skipping the delta)
- $violation that has a getPropertyPath() method that returns something slightly different (a string, starting with the delta)
Maybe we could remove the new param, and instead provide a dedicated helper method in WidgetBase, $this->getLocalErrorPath($violation), that implementations of errorElement($violation) would call ?
Comment #14
tim.plunkettSorry for letting this slip. Just wanted to also point out that we cannot dereference arrayPropertyPath/propertyPath, since it is somethings empty.
#2553983: Required textarea with summary breaks ajax events for other fields.
Comment #15
dbolinovski CreditAttribution: dbolinovski commentedRemoved $violation->arrayPropertyPath and added WidgetBase::getLocalErrorPath(ConstraintViolationInterface $violation).
Included fix from https://www.drupal.org/node/2553983.
Comment #18
dbolinovski CreditAttribution: dbolinovski commentedComment #19
dawehnerIdeally we would have test coverage for some of the following cases: a) a widget handling multiple form elements b) a widget on a field with single cardinality b) a widget on a field with multiple cardinality, so we are sure that its passed along properly to all of them.
Comment #20
yched CreditAttribution: yched commentedAgreed with #19, otherwise patch looks good to me :-)
Comment #21
bojanz CreditAttribution: bojanz at Centarro commenteddbolinovski has started work on tests, I've told him to upload his progress the next time he's online.
Comment #22
dbolinovski CreditAttribution: dbolinovski commentedTextareaWithSummaryWidget::errorElement() changes. Added test cases.
Comment #24
swentel CreditAttribution: swentel commentedShould be 'Contains \Drupal ...'
80 chars
80 chars
80 chars
Comment #25
nlisgo CreditAttribution: nlisgo commentedI will address feedback in #24.
Comment #26
nlisgo CreditAttribution: nlisgo commentedComment #27
bojanz CreditAttribution: bojanz at Centarro commented"Definition of" still needs to become "Contains" :)
We can go with something shorter perhaps. "Tests the validation violation property paths."
And the @group is clearly bogus.
dbolinovski was unsure about the test location and naming, would appreciate a comment there.
Comment #29
nlisgo CreditAttribution: nlisgo commentedFeedback from #27 has been addressed except for the invitation to comment on the location and name of the test.
Comment #34
dbolinovski CreditAttribution: dbolinovski commentedComment #35
dbolinovski CreditAttribution: dbolinovski commentedComment #36
dbolinovski CreditAttribution: dbolinovski commentedAddressing Bojan’s feedback, changed variable names to snake_case, removed assertion from setUp(), plugin annotation changes.
Comment #37
yched CreditAttribution: yched commentedDrupal\system\Tests\Validation is not ideal IMO, the tests in there are about our validation API, this is about field API widgets.
So I'd suggest putting it in Drupal\system\Tests\Field\Widget.
Other than that, looks good :-)
Comment #38
dbolinovski CreditAttribution: dbolinovski commentedAddressing #37, test moved in Drupal\system\Tests\Field\Widget.
Comment #39
dbolinovski CreditAttribution: dbolinovski commentedAddressing #37, test moved in Drupal\system\Tests\Field\Widget.
Comment #40
yched CreditAttribution: yched commentedWait, actually this is wrong :
Wait, actually that is wrong :)
The code in HEAD does an array_unshift(), which removes the "delta" part of the property path, so that the widget for delta N works with actual local path (that does not start with the delta).
The new code skips that, and getLocalErrorPath() will return a path that begins with "delta".
Mulling on that a bit.
Comment #41
yched CreditAttribution: yched commentedDiscussed with @tim.plunkett in Barcelona.
Initially this issue was opened as a task to cleanup a @todo for a not-too-nice but minor and internal hack (how WidgetBase::flagErrors() communicates violation paths that are local to each widget)
This was then risen to a major bug as it was thought to block #2509268: Inline errors repeated on child elements in module uninstall form, which had fails around that code area in WidgetBase, and that code area had a @todo pointing here.
- Fixing that @todo would however not actually fix the fails over there, which have more to do with how the specific UriWidget decides to implement its errorElement() logic (by just keeping the parent implementation), and the patch here wouldn't actually change that.
- The patch here would also be an API break at this point (WidgetInterface::errorElement() implementations couldn't rely on $violation->arrayPropertyPath as they currently do).
- Plus, the patch is currently broken, as it doesn't handle the deltas correctly :-)
So I'm very inclined to "won't fix" this at this point - thus, I'll temptatively do that. Feel free to argue otherwise :-)
Comment #42
yched CreditAttribution: yched commentedAnd actually doing it...
Comment #43
bojanz CreditAttribution: bojanz at Centarro commentedI understand yched's argumentation, the widget should be responsible for mapping errors to elements because the form structure is not guaranteed to match the property path.
However, this is not obvious at all from the current code, and should be documented.
Comment #44
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThis problem started to show up more an more with various widgets and people keep trying to "fix" it by changing the
errorElement()
method of each widget :/Comment #45
swentel CreditAttribution: swentel commentedOk let's see if this works.
Completely new approach which isn't API breaking at all. Added the test from #2614250: Number widget validation can break AJAX actions as test only, full patch and interdiff (which is just the "fix"). Not sure how the rest of the tests will react on this.
Comment #48
swentel CreditAttribution: swentel commentedOk, looks that was a little naieve, but also kind of impossible to figure out anyway after some more local testing ..
Comment #49
swentel CreditAttribution: swentel commentedComment #51
xjmI tried to summarize why this is a major bug based on the two related issues. Probably could use a more complete summary update.
It makes sense for this to be major if indeed multiple widgets fail validation and cause fatals with AJAX because of it.
Comment #53
DuaelFrThis issue is easily reproductible with the paragraphs module.
Expected: paragraph type form is embedded in the content type creation form
Current: AjaX error
Comment #54
andypostLatest patch still needs work
Comment #55
andypostReroll
Comment #59
geerlingguy CreditAttribution: geerlingguy at Midwestern Mac, LLC commentedI hit this just with the core number and core image field:
Luckily, in my case, the file still uploads/attaches properly, but it looks strange on the front end/admin UI.
Comment #60
xjmThe core committers and entity and field maintainers discussed this issue awhile back and agreed that it is major on account of the exceptions being triggered through the user interface. #2614250: Number widget validation can break AJAX actions is a specific case of this, also major, and currently postponed on this issue.
Thanks @geerlingguy for confirming the issue.
Comment #61
Dane Powell CreditAttribution: Dane Powell at Acquia commentedI've confirmed that #55 resolves this as well as #2614250: Number widget validation can break AJAX actions. It seems that the failing tests are unrelated to this particular patch.
Moving back to "needs review" to re-run tests, and also bumping target version back to 8.3.x since this should not be a disruptive fix.
Comment #62
Dane Powell CreditAttribution: Dane Powell at Acquia commentedRe-roll of #55, trying to get tests to run
Comment #65
Dane Powell CreditAttribution: Dane Powell at Acquia commentedOh yeah, the patch in #45/55/62 really is broken. It "fixes" the immediate issue but goes too far and removes legitimate validation (allowing you to e.g. create blocks with duplicate descriptions). Hence the failing tests.
Comment #66
jdleonard@Dane Powell: Moving this to 8.4.x-dev per my reading of the Backport Policy. Feel free to reset if I have misinterpreted. Also, hello/JIBA!
I believe the proposed fix in this issue will resolve the issue I just ran into, first reported by someone else in #2714347: No way to set error on a field from an entity constraint validator. Updating title to reflect increased impact of this fix (maybe someone can devise a more concise title) and updating the issue summary to reflect the additional major bug this will address.
Comment #67
Dane Powell CreditAttribution: Dane Powell at Acquia commentedMakes sense. When we finally get this merged, do I get to say that Jones Wins Again? :)
Comment #68
joe_carvajalWe're actually blocked into this issue too.
We reproduced the issue following the steps in #59, but the image in our case cannot be uploaded.
The casuistry to reproduce this issue is quite probable (only needs an entity with a file and a numeric field), and the issue prevents users to upload files when this happens. Shouldn't this issue be triaged as critical instead?
Comment #69
josephdpurcell CreditAttribution: josephdpurcell at Digital Bridge Solutions commentedI am blocked on this issue as well. I can confirm that with Drupal 8.3.5 and address module 8.x-1.0 that the "AddressFormatConstraintValidator" is not properly throwing violations, even though it correctly calls core's buildViolation().
I have tested patch #62 with no success.
Comment #71
Pascal- CreditAttribution: Pascal- at Open Up Media commentedThere's a really easy work around here, just make the Number (Integer) fields not-required and it instantly works again.
Not sure if other Number fields affect this.
Comment #72
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI'm sorry that my comment from #44 derailed the initial purpose of this issue :/
I've opened a separate issue with a concise summary of the problem that has been discussed in the latest comments and a proposed fix: #2901943: Content entity form validation does not respect the #limit_validation_errors property from field widgets, so let's get this issue back to what it was about before my comment. That would be @bojanz's request from #43 to improve the documentation of
\Drupal\Core\Field\WidgetBase::errorElement()
.Note that the "inability to set error on a field from an entity constraint validator" part of the current issue title was already fixed by #2894634: Allow entity-level validations to flag errors on form elements.
Comment #74
steveoliver CreditAttribution: steveoliver at Unleashed Technologies commentedI've read through this and all these related issues, and while we are making progress with documentation, and this issue does address the issue of setting the correct violation path, it still seems like the underlying issue is that the "if ($error_element !== FALSE) {" check in WidgetBase:L451 ends up letting NULL values through to the '$form_state->setError()' call on the next line.
Shouldn't we make sure that all implementations of errorElement actually return a valid element or FALSE? And/or change the "if ($error_element !== FALSE) {" check to "if ($error_element) {" to prevent NULL values from coming through, e.g. when an error element path can't be determined?
I'm trying to figure out which of all these issues to use to propose a new patch.
Comment #75
steveoliver CreditAttribution: steveoliver at Unleashed Technologies commentedMaking sure $error_element is actually an array, instead of just !== FALSE.
Let's see what testbot says.
Comment #76
steveoliver CreditAttribution: steveoliver at Unleashed Technologies commentedComment #77
bahuma20#75 works for me :)
Comment #87
needs-review-queue-bot CreditAttribution: needs-review-queue-bot as a volunteer commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.