Problem/Motivation
Currently, we run validation against fields on the entity forms that have widgets. However, if a field can be edited with a form element that is not a Field API widget, we do not validate its value at the field-level (i.e., check it against the field's constraints). The form element might have its own custom #element_validate, but since that's a custom function, there's no guarantee that it results in a value that satisfies the field's constraints.
Proposed resolution(s)
Ensure that all entity forms only use widgets for editing field values.
Remaining tasks
- Find all occurrences (if any) of core entity forms that edit a field value with a direct form element rather than a widget.
- If there are any such occurrences, fix them to use a widget.
- Write a change record and possibly other documentation letting contrib know to do the same.
Original report by @fago
Make entity form controllers embrace entity validation and map validation violations to form errors.
Comment | File | Size | Author |
---|---|---|---|
#39 | 2002180-entity-forms-skip-validation-37-do-not-test.patch | 1.9 KB | colinafoley |
#6 | entity-forms-validation-2002180-6.patch | 2.72 KB | klausi |
#6 | entity-forms-validation-2002180-6-interdiff.txt | 781 bytes | klausi |
#2 | entity-forms-validation-2002180-2.patch | 1.96 KB | klausi |
Comments
Comment #1
BerdirTagging all the things.
Comment #2
klausiNot sure what we want to do here and how it should happen.
First patch for experimenting. We do validate the configurable fields right now, so we should expand that to the entity base fields. Not sure how to map back the errors to the correct input field on the form ...
TODO:
* ->validate() should be called on the entity object, since there might be constraints registered on the entity level besides the field level constraints. Right?
* Field widgets for entity base fields (there must be an open issue somewhere) that can be flagged in the case of errors.
Comment #4
attiks CreditAttribution: attiks commented#2 You're right, validate has to be called on the entity, because an entity can define its own constraints
Comment #5
moshe weitzman CreditAttribution: moshe weitzman commentedRe: #2, perhaps you refer to #2010930: [META] Apply formatters and widgets to rendered entity base fields (all besides node.title) and its companion issue which converts node.title field.
Comment #6
klausiSame problem as in #2002158: Convert form validation of comments to entity validation, what should we do with empty string values that reach the primitive validator? Just ignore there as I propose here?
Comment #7
Berdir->get() always works on the NG entity, you don't need getNGEntity().
Maybe go up in the tree and add the error to the first element that we find?
title.0.value => no match.
title.0 => no match.
title => match.
That is I think kind of the inverse of buildEntity(), which does $entity->set($field_name, $form_state['values'][$field_name} and then the setValue() implementations are responsible for passing it through.
We could move that line into a separate method, then it's easy to override if you have a weird form structure. I don't think that will be common.
Do we actually have test coverage for this? (upload a patch that just removes this to verify? Possibly remove a few additional validations too.
In the NG conversion issue, I had to refactor this stuff quite a bit, as it is no longer possible to set an invalid reference. This still works around that by introducing a temporary ->name property that doesn't actually exist outside of the edit form.
Comment #8
BerdirComment #9
tim.plunkettComment #10
Anonymous (not verified) CreditAttribution: Anonymous commentedhow is this not critical?
Comment #11
jibran#6: entity-forms-validation-2002180-6.patch queued for re-testing.
Comment #13
effulgentsia CreditAttribution: effulgentsia commentedRestoring tags unintentionally dropped in #11.
Comment #14
yched CreditAttribution: yched commentedSo yeah, currently we only run field constraint validation on fields where we know how to report the errors back - that is, fields that are rendered through widgets. This now can include some base fields (only node title for now, probably more to come).
#2095195: Remove deprecated field_attach_form_*() moves this step to a separate validateWidgetsValues() in a separate helper class usable by other forms than EntityFormControllers.
The rest of the fields:
- Might be rendered by arbitrary ad-hoc FAPI code in EntityFormController or hook_form_alter(). A rule like the one suggested by @Berdir in #7.2 might be applied to "best guess" where the error should be reported. I don't think the violation messages contain the field label currently, so it needs to be prepended so that the displayed errors make sense.
- Might just not be present in the form. They still can raise violations for some reason (e.g constraints have changed since the entity was last saved). We do not want to raise form errors for those, that would block form submission on stuff the user cannot fix himself in the form.
Also, it's been agreed in #2070429: Configurable fields aren't validated against the "required" constraint except in forms that field validation should not run on fields on which FAPI-level errors have already been reported, because:
- that might cause double report (eg required checked by both #required and a field constraint)
- the presence of FAPI errors for the field means the submitted form values are invalid/broken to begin with, and we shouldn't even try to turn them into actual model values in the entity.
Comment #15
fagoThe best guess of 7.2 shows that we lack a direct mapping of form elements to the field properties here. Though, I'd do the guessing the other way round and start with the first property + propagate down so far as possible.
Howsoever, this won't work if we've some sort of nested structures. I think we can write a form API helper that goes through the tree to look up the form element responsible for certain form #parents though.
Yes, but we need to present errors for stuff like entity changed validation. But I think we could just pass through all violations that are flagged on "entity" level (=using an empty property path) + make sure the changed violation gets flagged as such.
Comment #16
effulgentsia CreditAttribution: effulgentsia commentedSee also #2111887-28: Regression: Only title (of base fields) on nodes is marked as translatable. For both content translation UI and validation error reporting, I think we'll want to replace all entity form elements that correspond to fields with widgets in #2010930: [META] Apply formatters and widgets to rendered entity base fields (all besides node.title). If we want a fallback along the lines of 7.2, then I think simply a form_set_error() of the top-level violation path (i.e., just 'title') is a good start: we could refine from there if needed, but I don't think we'll need it. Since form_set_error() is based on #parents rather than #array_parents, something like form_set_error('uid') will correctly flag $form['author']['uid']. It would also automatically flag all children of that. So the only problem would be if there are some children we don't want flagged, but that means we have a multiproperty base field not using a widget: an edge case, and one that can be fixed by converting it to use a widget.
Comment #17
jibranNeeds reroll.
Comment #18
Sutharsan CreditAttribution: Sutharsan commentedReroll needs knowledge of what to do with the changes in
Drupal\Core\Entity\EntityFormController::validate
. This method has drastically changed.Comment #19
fagoI've discussed ignoring violations more with webmozart recently. We came to the (sad) conclusion, that we cannot do it safely - as we cannot ensure which fields the constraint have been using for the validation. The only safe approach seams to be enforcing having a valid entity *before* starting to edit it.
Is that feasible to implement? I guess it means we'd have to check for entities violation new constraints when e.g. configuring valid integer ranges :-/ Outdated entity references could be filtered out before the entity is loaded into the form. Formatter references suck a bit, but it should be possible to handle that similarly, e.g. update the reference as suiting.
Comment #20
yched CreditAttribution: yched commentedIs this a reply to the current agreed approach of "field validation should not run on fields on which FAPI-level errors have already been reported" ?
There might be some entity-level/cross-field constraints, but the vast majority of the constraints are specified on isolated fields. For those, we totally have the ability to skip them.
.
Seems hard to reach. We cannot prevent UI or direct API/config changes from changing "max allowed value" to 10 because there exists an entity with value 15 somewhere ?
Comment #21
fagoI know and I really dislike it, but it's the only way I see a clean/safe solution. I'd be happy to be convinced of the opposite.
Nope, that's not so problematic imho as validation errors are reported anyway. Just if we pass validation, we must be sure we have not dismissed a valid violation.
I don't think we can assume constraints on fields only look at those fields, thus we'd not know what to skip. But as said before, this shouldn't be so problematic.
I've been thinking about doing a "diff of before and after violations", but unfortunately you cannot be really sure the old and new violations stem are identical as they might be caused by different value combinations.
So yeah, for max/min range constraints we'd have to do an entity query for looking up violating entities and disallow changing it if there are any I guess.
Comment #22
SamiHamzaoui CreditAttribution: SamiHamzaoui commentedComment #23
andypostThe primary trouble here is to find proper form element by using
$violation->propertyPath
Comment #24
catchComment #25
jessebeach CreditAttribution: jessebeach commentedThere are probably overlaps with this issue #1493324: Inline form errors for accessibility and UX.
Comment #26
fagorelated: #2023465: Allow validation constraints to add in message replacements
Comment #27
JacobSanfordPatch in #6 applies cleanly for me. Removing 'Needs reroll' tag and retesting.
Comment #28
JacobSanford6: entity-forms-validation-2002180-6.patch queued for re-testing.
Comment #29
JacobSanfordMy mistake, I was squelching file-not-found errors. Resetting tag.
Comment #31
colinafoley CreditAttribution: colinafoley commentedI've been chasing this around and I think #2278403: Remove uses of form_execute_handlers() is also related (and that this issue blocks that #2278403: Remove uses of form_execute_handlers()). EntityFormController.php has been removed, so the previously submitted patches are a bit moot. I think the patch should really be implemented in EntityForm::validate() now.
The previous patches sort of indicate a proposed resolution: we want to have a generalized approach to validating fields on entities. The generalized approach would be to loop through field_defintions on the entity and check them against their constraints. From patch #6:
Then things like NodeForm can just call parent::validate() (which is ContentEntityForm), and its parent would call grandparent::validate() which finally gets us back to EntityForm::validate().
Question: Is there ever a case that there would be form elements that are not field-driven but also need validation? Perhaps in EntityForm::validate() we don't need this case because subclasses will take care of the specifics that might be outside of field-driven form elements, if they exist.
If I can get some confirmation that my assessment is correct, I'll update the Issue Summary with more details.
Comment #32
colinafoley CreditAttribution: colinafoley commentedAnother update for anyone new looking at this: EntityFormController wasn't removed, it was renamed:
See #2238077: Rename EntityFormController to EntityForm.
Comment #33
fagoThis is already done as long as fields are using a widget. However, afaik we miss adding running validation for fields that do not have widgets right now. So what's left is
- creating a change record to make clear you should do a widget for any custom form
- improve the validation flow to ensure to violations are missing
-> Rephrasing the issue title accordingly
Comment #34
colinafoley CreditAttribution: colinafoley commentedComment #35
colinafoley CreditAttribution: colinafoley commentedComment #36
fagoDiscussed this a bit more with effulgentsia and we figured something along the following lines would be good to deal with violations:
- Validate the whole entity, assume it has to pass validation
- Show all violations by default, add a hook which allows contrib to dismiss violations. That way core is safe by default, but site builders can create not-fixable forms by invalidating data. If people want to support editing invalid data the hook still allows them to do so.
Comment #37
colinafoley CreditAttribution: colinafoley commentedIt seems like we're trying to account for different scenarios as we think about this. Do we have tests written for Entity validation? We should write tests so we know what it is that's breaking, then we can fix it.
I'm going to work on the Issue Summary and an initial patch based on our current direction.
Comment #38
colinafoley CreditAttribution: colinafoley commentedComment #39
colinafoley CreditAttribution: colinafoley commentedThis is a very early patch (especially considering that I don't feel we've actually decided on how the validation should be run). Please see the comments for direction for the next steps in the patch.
This is a patch based on the proposed resolution of validating the entire entity.
EDIT: My bad, messed up the patch name's comment number.
Comment #40
dawehnerSo this sounds more like solution two, do you want to update the IS to point people to the way how it could be solved?
Comment #41
catchTagging.
Comment #42
catchComment #43
alexpottAre we sure that we don't want all fields to have widgets? See #2378947: Hiding a field using form modes should not remove it from the form object
Comment #44
effulgentsia CreditAttribution: effulgentsia commentedI discussed this with @catch and we decided that dealing with fields that can't be edited on the form at all is a different scope than fields that can be edited but are skipping validation due to using a custom form element rather than a widget. We also decided that only the latter is Critical priority. So, I opened #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay as a Major issue, and updated the IS of this one to a smaller scope.
If anyone disagrees with this, please comment.
Comment #45
effulgentsia CreditAttribution: effulgentsia commentedThe issue summary in #2010930: [META] Apply formatters and widgets to rendered entity base fields (all besides node.title) has a list in its "Remaining Tasks" section. Would be good for someone to confirm if that list is accurate and complete.
I considered making that meta issue and its children critical to match the priority of this issue. However:
So, I'm not making those issues critical at this time, but I do think that attempting to complete them is a good way to resolve this issue, unless for some reason it turns out not to be.
Comment #46
effulgentsia CreditAttribution: effulgentsia commentedSee also #2227381-44: Apply formatters and widgets to User base fields 'name' and 'email' for a specific example where we should decide on the relative merits of "use a widget" vs. "use custom validation". And possibly make a decision there that applies to the other remaining examples in core.
Comment #47
xjmComment #48
plachComment #49
fagoWhile converting all fields to use widgets is definitely a good movement, it will help to work-a-round this issue to apply, but not solve/fix it; i.e. every contrib not leveraging widgets for some reason (e.g. there is no way to do a way across multiple fields) would run into the same problem. Then, as commented in #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay I do not think this is enough to make sure we do not miss some validation logic in entity validation / validation via REST as it won't be able to cover conditional / combined validation (validation of field A in dependence of field B). Thus, I think we should solve #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay and while doing that provide an easy way to map those violations to form elements, so we can cover not (yet) converted base fields as well.
Comment #50
BerdirWhat is the difference of this issue vs. #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay?
Wouldn't #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay fix this as well? Why do we need two criticals to track this?
Comment #51
fagoSee #44 for how we ended up with two issues - the second one was not intend as critical but got bumped up.
I agree that the other is going to cover this one as well, thus I'd be fine with closing this and tracking progress in the other only.
Comment #52
larowlanbased on #51
Comment #53
larowlanDiscussed with @xjm agreed is a duplicate of #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay, will update IS over there.
Comment #54
xjmThanks @larowlan!