Problem/Motivation
If the EntityFormDisplay::validateFormValues finds any violations then it does :
$form_state->setErrorByName('', $violation->getMessage());
.
However the form element is provided with the parameter $form and it is better to use it instead of setting an error in the form state by name with an empty name string.
Proposed resolution
Replace $form_state->setErrorByName('', $violation->getMessage());
with
$form_state->setError($form, $violation->getMessage());
.
FormState::setError calls setErrorByName and builds the name by imploding the #parents of the given element. For simple entity forms there will be no change as $form['#parents'] will be always an empty array, but complex and nested form widgets will profit from the change by correctly setting the error on the element caused validation failure.
Remaining tasks
Review.
User interface changes
For nested and complex widgets on validation the element, for which the validation has failed, will now be highlighted with an error.
API changes
none.
Data model changes
none.
Comment | File | Size | Author |
---|---|---|---|
#14 | interdiff-10-14.txt | 3.29 KB | hchonov |
#14 | 2767125-14-test-with-fix.patch | 6.17 KB | hchonov |
#14 | 2767125-14-failing-test.patch | 5.4 KB | hchonov |
#7 | 2767125-7-test-with-fix.patch | 6.11 KB | hchonov |
Comments
Comment #2
hchonovComment #4
hchonovUnfortunatelly there no complex and nested inline entity forms in core and so I cannot think of a suitable test for this issue where the parents will be set in the given form array for any module in core. If anyone could think of such a case please explain and I will write the test.
Comment #5
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI did a bit of digging around this code and it seems that the
->setErrorByName()
call with an empty string as the first argument was introduced in #2395831-102: Entity forms skip validation of fields that are not in the EntityFormDisplay, where it replaced a call on an actual element path:$form_state->setErrorByName(str_replace('.', '][', $violation->getPropertyPath()), $violation);
.So I think that the patch is correct by assigning the error to the $form element instead, because this indeed takes care of complex use cases and allows embedded entity forms to report errors correctly.
Comment #6
alexpottI think it is worth having test coverage here because
Comment #7
hchonovAdded test coverage.
Comment #10
hchonovCleared some oversights.. Should be better now.
Comment #13
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThe test looks great! Just a few minor things that should be fixed before RTBC:
Missing member visibility, i.e. 'public' :)
This
enforceIsNew()
is not really needed, you can just call theid()
method on both entities when you need it below."in" the form :)
I think our coding standards say that we need to put () at the end of method names in code references.
Comment #14
hchonov@amateescu Thank you for the review. I've addressed all your remarks.
Comment #15
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedAwesome, looks great now :)
Comment #18
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedComment #20
tim.plunkettComment #21
hchonovIt has been a random failure, so moving back to RTBC as it has been done by @amteescu in #15.
Comment #22
alexpottCommitted and pushed 505eb55 to 8.3.x and 6633476 to 8.2.x. Thanks!
This is not introduced here but you only set one error per element - see
\Drupal\Core\Form\FormState::setErrorByName()
. So I think this code is flawed but it is less flawed than before.Whilst correct, this change is out of scope. Reverted on commit.
Comment #25
alexpottWe should have a follow-up for #22.1
Comment #26
hchonovalexpott, do you mean that we need a follow-up issue for extending FormStateInterface::setError and ::setErrorByName to be able of setting multiple errors on an element instead of only one?
Comment #27
hchonovI've created a follow-up for #22.1 - #2818437: FormState::setErrorByName does not set multiple errors on the same element.