Problem/Motivation
This issue was originally Comment-specific. But further investigation has revealed that the problem is not specific to comments. It's a general Entity/Field API problem. Entity constraints are validated before field constraints. See #12 and later.
Still, the Comment use case is a useful guide through the discovered problems:
While working on #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method, it took me hours to figure out what the minimal set of fields is that one must send for a Comment entity to be successfully created. See #2737719-75: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method.
In doing so, I had to do extensive debugging using Xdebug to figure out the incredibly obtuse error responses, which are categorically HTTP 500 responses instead of 422 responses. And not only that, but the message in each of those responses is absolutely worthless. It doesn't give any indication about what is wrong.
AFAICT key problems are:
\Drupal\comment\CommentStatistics::update()depends onentity_type, which is neither required nor validatedCommentNameConstraintValidatordepends onentity_idandfield_name, but runs before either of those can even possibly be validated (entity_idis required, but that's not validated beforeCommentNameConstraintis validated)- Both of these result in failures outside of what the Entity API validation system is designed for, hence resulting in undecipherable errors in REST responses
Another use case was reported by @joachim in #59+#61, this time the problem is not "entity constraint validated before field constraint", but "field constraint validated before property constraint":
Over at Duration Field module, we have a field type with two properties:
- duration, which is a PHP duration string such as 'P1M1D'. This uses a custom data type.
- seconds, which is an integer count of seconds.The duration data type has a constraint to check its value is a correctly-formed PHP duration string. So '1D' is not valid (must start with 'P'), 'P1X' is not valid ('X' is not a valid interval letter), 'cake' is not valid for obvious reasons. Setting it as deeply as on the data type makes sense, because the data type should enforce this wherever it's used.
I wanted to add a constraint to the field item as a whole, to enforce that the durations and the seconds values both match. In other words, prevent a field being saved with something like 'P1D' for the duration and '60' for the seconds (one day is not 60 seconds long!)
However, the constraint on the field item runs BEFORE the constraint on the duration property. So the field item constraint might run with badly-formed values for the duration property! It would therefore need to perform its OWN check on the format of the duration value, which is just repeating the work that the duration data type constraint is going to do again later on.
More generally, this problem will occur anytime you have a compound field type where:
- one or more of the field's properties properties need validation for their value alone
- the whole field item needs validation as a whole
Proposed resolution
Unknown, see comments.
In both use cases above, the observed pattern is: the more granular thing should be validated before the more coarse thing, because otherwise the data in the more coarse thing cannot be assumed to be valid, thus invalidating its validation 🤓
Remaining tasks
MakeComment'sentity_typebase field is required: #2885809: The 'entity_type' and 'field_name' base fields on Comment are required- Make
Comment'sentity_typebase field have a validation constraint, to ensure a valid entity type is set. Test coverage must include a non-existing entity type to ensure good DX. - Also ensure that
\Drupal\Tests\rest\Functional\EntityResource\Comment\CommentResourceTestBase::testPostDxWithoutCriticalBaseFields()is no longer sending anAcceptrequest header. - … more, but yet TBD!
User interface changes
TBD
API changes
TBD
Data model changes
TBD
| Comment | File | Size | Author |
|---|---|---|---|
| #52 | interdiff-2820364.txt | 6.96 KB | dawehner |
| #52 | 2820364-52.patch | 20.13 KB | dawehner |
| #49 | interdiff-2820364-47-49.txt | 1.96 KB | mohit_aghera |
| #49 | 2820364-49.patch | 17.63 KB | mohit_aghera |
| #47 | 2820364-47.patch | 16.66 KB | mohit_aghera |
Comments
Comment #2
wim leersNote that #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method introduced test coverage to show the current pain, and show the responses that should be sent.
Postponing on that landing.
Comment #3
wim leers#2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method landed!
Comment #4
wim leersThe assertions introduced by #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method to verify that this is indeed broken on all PHP versions was itself broken, due to behavioral differences between PHP 5.5, 5.6 and 7.0. That resulted in failing tests on 5.6. Hence there's a critical issue to make the assertion of fatal failure work on 5.6: #2832013: CommentResourceTestBase::testPostDxWithoutCriticalBaseFields() always fails on PHP 5.6 & MySQL 5.5 (works fine on other PHP versions).
Now working on this fix, so we can get rid of that "fatal failure assertion" entirely.
Comment #5
wim leersSo, this is the goal. But in order for this patch to be green, we'll need to fix the underlying bug.
Comment #6
wim leersThat is the most nasty one that we need to fix (per #2832013: CommentResourceTestBase::testPostDxWithoutCriticalBaseFields() always fails on PHP 5.6 & MySQL 5.5 (works fine on other PHP versions)), but there's actually more to fix.
This is the complete goal.
Comment #9
wim leersThe first of those (forgetting to specify
entity_type) results in a failure atBecause
$comment->values['entity_type']['x-default'] === [], which results ingetCommentedEntityTypeId() === NULL, which then results in\Drupal\Core\Entity\EntityTypeManager::getDefinition()throwing aPluginNotFoundException.But, note that that code is called by
\Drupal::service('comment.statistics')->update($this);(), which is called from\Drupal\comment\Entity\Comment::postSave(), i.e. after theCommententity was already saved! So, frighteningly, setting noentity_typevalue throws no exception whatsoever! Fortunately,\Drupal\Core\Entity\Sql\SqlContentEntityStorage::save()then rolls back the DB transaction. So, no harm is done. It throws anEntityStorageExceptionwith the messageThe "" entity type does not exist..That
EntityStorageExceptionis caught byEntityResource::post(), which converts it to a 500 response:Next: figuring out a solution for this.
Comment #10
wim leersYou can test this locally by importing this config:
and running this CURL command:
which should result in this output:
Comment #11
wim leersMaking the
entity_typebase field required already helps. The first assertion now works.But… it's not enough. Making a field required does not ensure it contains a valid value yet. And unfortunately, that's exactly the problem: if we now pass a non-existing
entity_type, it will still fail:Again because of a
PluginNotFoundException(because thefoobarentity type plugin does not exist), which is converted to aEntityStorageExceptionet cetera.Comment #12
wim leersMoving on to the next one: missing
entity_id.We've got a serious infrastructure problem here…
entity_idis already marked as required. But, unfortunately, theCommentNameConstraint(introduced in #2002158: Convert form validation of comments to entity validation, with additional test coverage inCommentValidationTest) constraint runs first. And it usesentity_id(as well asfield_name). But at this point, no validation has run yet to verify thatentity_idis set! So, it's pretty much completely pointless thatentity_idis required, because you could never ever get the corresponding validation error (entity_id: This value should not be null), since theCommentNamevalidation error will always be shown instead, since its constraint validator runs first.CommentNameconstraint, because it covers multiple fields:nameanduid. So we can't move it to the field level instead of the entity level.So… how to fix this? The only thing I can think of is to expand what
CommentNameConstraintValidatorvalidates: let it also validate theentity_idandfield_namefields. But the right solution would be to letCommentNameConstraintValidatorsomehow indicate it requires theentity_idandfield_namefields to already be validated before it can run, because those being valid is a hard requirement.Unassigning, this needs a Symfony 2 Validation component expert.
Comment #13
wim leersComment #14
tstoecklerWell it does that, simply because Symfony's Constraint Iteration does it that way. It does seem like a pretty severe issue, and I do think we should at least consider turning that around. Not sure if that's possible with BC, though.
Will look at the patch in detail tomorrow.
Comment #15
wim leersI don't think we need to turn it around!
I think it needs to be possible to express dependencies. And in fact, we already have much of the necessary infrastructure!
\Drupal\comment\Plugin\Validation\Constraint\CommentNameConstraintcontains this:What we need on top of that is
ConstraintDependencyInterface(better name TBD), which allows that class to also specify:Or perhaps this belongs in the
@Constraintannotation:That would ensure that when validating the
CommentNameConstraint, we'd first run any constraints for those lower levels (fields). Which could absolutely be a pure API addition, without BC break.Comment #16
tstoecklerComment #17
dawehnerIsn't the patch below enough to turn around the validation process to go deep into the tree first, before validating entries at the upper level?
Comment #18
tstoecklerYes, I think so. If that's not considered an API change, I would be fine with that.
The most prominent entity-level validator we have is
EntityChangedConstraintValidatorso with this all field-level validation would run for entities with changed-tracking only to then realize that regardless of any violations the entity cannot be saved anyway because it has been changed in the meantime.I guess that's not really a problem, because the 80%-case is that there is no changed violation, so that the field-level validators are run anyway, but I'm also neither an expert in nor a heavy user of the validation system, so I'm not sure if there are other cases that I'm missing.
Comment #19
wim leersComment #20
fagoThe current processing order is something we just inherited from symfony, it wasn't done by any purpose. Thinking about it, I'd agree that a dept-first approach makes more sense. So the individual fields are covered first and combined-validation second.
However, the changed validation is a good example that this ordering is not the perfect fit always. It would be nice if one could prioritize EntityChanged validation and run others second. We might be able to use the validation groups feature for that - right now we do not use them. E.g. we could define some "Priority" validation group and run constraints assigned to this group first. See https://symfony.com/doc/current/validation/groups.html for more about groups.
Still, that's an optimization which would be a nice to have, but does not seem to be really necessary. That said, I think a good way to move forward here is to turn validation order around and to create a follow-up feature request for the priority validation group.
Speaking of API changes, I think this it's ok to do. The traversing order is something not documented, any nothing should rely on it. So what can and will change in some situations, is the order of the violations received - but as this issue shows, the depth first traversal should lead to the better violation ordering in *most cases*.
Setting to needs review, to see what the test bot says.
Comment #23
wim leers@fago: thanks for the review!
Only 6 failures. That means this is causing few regressions, if any, and it's probably feasible.
Now let's see if can help us fix the terrible Comment POSTing DX. As I explained in #11, making
entity_typerequired allows us to pass the first test intestPostDxWithoutCriticalBaseFields(). The second test is testing a missingentity_id. And that's what I pointed out in my analysis in #12 as being the one to suffer from the current validation order (top-down instead of bottom-up).Unfortunately, that's not what happens. The patch in #17 does result in a "This value should not be NULL" (aka required field has no value) constraint violation message for
entity_id. But … it still allowsCommentNameConstraintValidatorto continue, despite that needingentity_idto have a correct value. This makes me suspect that in this Symfony Validator component, there's no concept of "only execute this constraint validator if all prior/basic ones did not complain"?But then if I go and look at the docs, they do support this: http://symfony.com/doc/2.8/validation/sequence_provider.html seems a perfect fit, or otherwise http://symfony.com/doc/2.8/validation/groups.html. Entity validation experts, back to you!
In this patch, I:
entity_typea required base field; this allows me to make the first test intestPostDxWithoutCriticalBaseFields()to pass again. This does NOT exercise the changes proposed by @dawehner.testPostDxWithoutCriticalBaseFields()to its desired state. I made this pass by modifyingCommentNameConstraintValidatorto only run if bothentity_idandfield_nameare not empty (note that this is not sufficient — they should be guaranteed to be valid at this point). The correct solution would be to use a "sequence provider" AFAICT.CommentNameConstraintValidatorshould be reverted.Comment #25
wim leersThis also causes the following change in error response. I'm not sure this is correct/desirable. But that's just a detail in the grand scheme of things here.
Comment #27
wim leersComment #28
wim leersThis is now also blocking #2768651: Let TimestampItem (de)normalize to/from RFC3339 timestamps, not UNIX timestamps, for better DX :(
Summoning @fago again.
Comment #29
amateescu commentedNote that it's not enough to change the base field definition, you also need an update function that does the same thing. See
aggregator_update_8200()for an example.Comment #30
wim leers#29: thanks :) But until we have a patch that actually works just for fresh installs, that is not something we need to think about yet! Hopefully we'll be able to write an update hook soon!
Comment #31
dawehner... yeah what?
Comment #32
wim leersWe just discussed this in an call. The consensus is that this is major due to the incorrect order in which validation constraints are checked.
However, it's difficult to see what the original problem (wrt
Comments) ends and the general problem begins. So it was determined that the first task is to have a separate issue for making theentity_typebase field on theCommententity type required. Did that: #2885809: The 'entity_type' and 'field_name' base fields on Comment are required.Reading this issue now, that means doing a subset of the patch in #11. But it'll still retain the majority of the original problem. At least it will be slightly smaller.
Also retitling to reflect the intended scope.
Comment #33
wim leersIS updated.
Comment #34
dawehnerI tried to understand this change again. Could it be that we need to skip validating in case there has been any errors on a more deeper level to make this fix actually properly?
Comment #35
wim leersYes — if there's an error at the field-level constraints, the entity-level constraints should not be executed, because the entity-level constraints assume valid field values.
Comment #36
dawehnerSo what about this particular change?
Comment #38
dawehnerOops, this is filed agains the wrong version.
Comment #40
dawehner... Upload a new patch
Comment #43
wim leersGreat comment.
Might also be good to add a comment to the class-level docblock, explaining that this validates depth-first.
Comment #44
dawehnerI tried to, but this throws exceptions all over the place.
In the meantime I fixed the failing validation in comment module, ... we no longer need to stop early.
Comment #47
mohit_aghera commentedRe-rolling patch for 8.6.x branch.
Patch was failing for CommentResourceTestBase.php and UserResourceTestBase.php files.
There are some failures in issue https://www.drupal.org/project/drupal/issues/2580551
I think fixing this may fix failures in https://www.drupal.org/project/drupal/issues/2580551 as well.
Comment #49
mohit_aghera commentedFixing couple of test case failures.
Comment #51
borisson_This comment reads weird, but I'm not sure how to improve it either.
Should be only one empty line.
^
/s/ValidEntityType/EntityTypeConstraint/
Double space before
extendsDouble empty lines.
Should we catch the exception here and add that as a violation instead? Also, are we ok with relying on php's cast of NULL to FALSE here? We could be more explicit by doing
if ($this->... === NULL) {}This is unrelated, I don't really mind, but I think we should remove this.
Comment #52
dawehnerThank you @borisson_ for the review! I fixed your points and started to look into the test failures.
This one is hard. As we now validate on the innermost level we stop validation on higher levels. I think its fine to change the expectation.
Comment #55
berdirUploaded a patch to #2885809: The 'entity_type' and 'field_name' base fields on Comment are required that works for the existing test coverage for comment.
From the entity API perspective, only very few fields are really critial (bundle, language, ..). I'm not convinced that we need a complex technical solution that has its own drawbacks (e.g., before you got more or less every validation error at once, now it's more likely you need multiple iterations to figure out everything that's wrong).
So I'm not convinced that we need or even can create a generic solution for this. I'd be perfectly happy to just document that specifically validation constraints (but kind of everywhere, because especially entity reference can easily get out of sync as we have no generic system to purge reference data if entities are deleted) should make sure that all the data they require is present and be written in a resilient way. Fixing the one specific validator for comments is trivial as my patch over there shows.
It will never be perfect, it would also be easy to work around the entity type validation here, you just need an invalid entity type/id combination or even entity type + id + field name combination and that validator will result in exceptions.
Comment #56
amateescu commentedAgreed with @Berdir here. This change is somewhat hard to achieve, has drawbacks as detailed in #55 and the benefits are therefore questionable.
My opinion is that we should close it as "won't fix".
Comment #57
wim leers#2885809: The 'entity_type' and 'field_name' base fields on Comment are required landed!
But I think we still want this?
To be fair, the most important part of this issue has now already been solved in #2885809: The 'entity_type' and 'field_name' base fields on Comment are required, meaning that I think we can downgrade this from major to normal.
Comment #59
joachim commentedThe issue summary doesn't seem to reflect the work on the patch, where there is discussion about allowing constraints to define dependencies on each other.
This affects other things than comments too. I wanted to add a constraint to the duration field type (#3067478: add validation that the duration in seconds and the PHP duration match), but because the data type constraints on properties are run first, it's not going to work. Generally, there are cases where you want constraints on the properties of a field, and then also a constraint on the field value as a whole to check it makes sense. The deeper constraints should run first.
Comment #60
wim leers#59: Excellent, I was hoping more people would comment here and report other cases where this causes problems!
I think that's the additional impact we needed to hear about to justify bumping this to .
It'd be wonderful if you could update the issue summary with your perspective, with the use cases you see. 🙏
Comment #61
joachim commentedOk I'll try to expand on the use case I saw here first, as I don't feel competent to wade into the issue summary...
Over at Duration Field module, we have a field type with two properties:
- duration, which is a PHP duration string such as 'P1M1D'. This uses a custom data type.
- seconds, which is an integer count of seconds.
The duration data type has a constraint to check its value is a correctly-formed PHP duration string. So '1D' is not valid (must start with 'P'), 'P1X' is not valid ('X' is not a valid interval letter), 'cake' is not valid for obvious reasons. Setting it as deeply as on the data type makes sense, because the data type should enforce this wherever it's used.
I wanted to add a constraint to the field item as a whole, to enforce that the durations and the seconds values both match. In other words, prevent a field being saved with something like 'P1D' for the duration and '60' for the seconds (one day is not 60 seconds long!)
However, the constraint on the field item runs BEFORE the constraint on the duration property. So the field item constraint might run with badly-formed values for the duration property! It would therefore need to perform its OWN check on the format of the duration value, which is just repeating the work that the duration data type constraint is going to do again later on.
More generally, this problem will occur anytime you have a compound field type where:
- one or more of the field's properties properties need validation for their value alone
- the whole field item needs validation as a whole
Comment #62
wim leersDon't underestimate yourself! :)
I think this is plenty clear as-is. I'm just quoting the entirety of #61 into the issue summary 😀
Comment #63
joachim commentedIt occurs to me that the way to summarize this issue is this:
Validation constraints for complex data (such as entities, fields with sub-properties, etc) should be able to rely on the values of the different data elements being valid themselves.
Comment #64
wim leersPlease add that to the issue summary! 👍
Comment #66
jantoine commentedI've also come up against this issue.
We have a Time Entry entity type that has Start Time, End Time and Duration fields, all of which are required.
We have field level constraints ensuring that the start time does not come before the end time. We also have entity level constraints ensuring that a new time entry doesn't overlap an existing time entry in any way. In total we have five different constraints at different levels, all of which are run upon validation.
If the Start Time, End Time or Duration fields have invalid data, it makes no sense to run validation further up the chain testing for sequential datetimes and overlaps with other time entry entities.
For now we'll have to implement the same checks in all of our constraints and skip the current constraints checks if the previous constraints checks don't pass, similar to how #2885809: The 'entity_type' and 'field_name' base fields on Comment are required was resolved.
Comment #71
wim leersCame up again in
\Drupal\ckeditor5\Plugin\Validation\Constraint\SourceEditingRedundantTagsConstraintValidator.Comment #72
berdirMy opinion is still #55 and that this is a non-major documentation issue. The title is also wrong IMHO. It's not (only) about the order. What we do is run *all* validation plugins and return all their results and what the old patch here proposes is stopping early and not running all of them.
As mentioned in #55, that comes with drawbacks, specifically that you don't know if you got all the validation failures when you call validate once(), which could result in possibly multiple iterations of fixing things.
Comment #73
wim leersAgreed that we should continue to return *all* validation errors!
But wouldn't depth-first still be preferable? 🤔
I apologize for rigidly pushing for a certain direction ~5 years ago. Let's get this back on track, and let's retitle this issue as you ask! But how?
CommentNameConstraintValidatorneeding a valid entity ID. I realize that not every validator needs other things to have validated already. Maybe we can optionally allow a validator to indicate which nested values need to have been validated already?→ title could be "Allow validation constraints to specify which nested values must be validated first"
→ title could be "Update CommentNameConstraintValidator to specify a validation sequence"
Comment #74
berdir> But wouldn't depth-first still be preferable? 🤔
I don't really see what difference it would make. Even if the deeper constraints fail, we still run the others and they still need to consider invalid data?
I don't know if there are any constraint features that we could depend on for specific dependencies or something, but being able to return all validation results in one go seems to contradict any attempt at not running some constraints under some conditions?
Comment #75
joachim commented> Even if the deeper constraints fail, we still run the others and they still need to consider invalid data?
In the cases here, no, you wouldn't.
For example, suppose you have a datetime field, and on the field you have a validation to say that it must fall on a Sunday.
User enters '34' for the day of the month. Validation fails on that property. You don't validate the whole date, because the data is meaningless.
The same applies to the comment problem. If the entity_type is invalid, there is nothing you can work with.
Comment #76
wim leers+
Exactly!
Comment #77
wim leersI wrote this in #73.2:
I investigated this today, to check if this is viable to use today 🤓
Conclusion: unfortunately not.
Because Drupal core does not use
\Symfony\Component\Validator\Validator\RecursiveContextualValidator implements ContextualValidatorInterfacebut\Drupal\Core\TypedData\Validation\RecursiveContextualValidator implements ContextualValidatorInterface, and the Drupal implementation does:meaning: Drupal hardcodes the default group and doesn't auto-resolve it into the concrete set of groups specified for the class, which is what Symfony's implementation does:
This means that today, it is impossible to use
GroupSequenceProviderInterfacetoday. Even though that would AFAICT be possible without changing any other API. If we'd port theif (isGroupSequenceProvider)branch into Drupal's recursive validator, it would be possible. (EDIT: that's not possible 1:1 because Drupal usesclass TypedDataMetadata implements MetadataInterface, andMetadataInterfacedoes not have "group sequence" methods, onlyClassMetadataInterfacedoes.)Why? Quoting @fago from 2015 in #2343035-155: Upgrade validator integration for Symfony versions 2.5+:
I think this issue proves there is a reason to support it. Which @dawehner also said at #2343035-159: Upgrade validator integration for Symfony versions 2.5+.2:
Note that that
Comment #78
wim leersConsidering #77, I think we need to move this to the
typed data systemcomponent.Comment #79
berdirReally not familiar with that code, but are you sure that will allow us to actually define groups or anything else more complex across field/property boundaries?
From a quick glance, I very much doubt it. \Drupal\Core\TypedData\Validation\RecursiveContextualValidator::validateNode uses recursion to first validate itself and then its children. At no point is there ever a constraint list for every field and their properties.
Comment #80
wim leersI think you're right 😔 The "group" functionality only works for executing multiple validation constraints on one node in a specific order. But in this case, it's child nodes that would need to be validated first. 😬
Comment #83
jonathanshawIt's not clear what the problem is that we are trying to solve here.
Lower-level constraints are still being validated recursively even if high-level constraints report violations, so changing the sequence of validation will not lead to changes in what violations are reported for high-level validation. Nothing valid becomes invalid, or vice versa, if we change the order of execution.
I can see 2 problems that are still relevant:
The DX problem: Entity/field Validators have to be coded defensively, they cannot assume their fields/properties are themselves valid and in some sense this is not DRY. (see #61)
The UX problem: Higher-level entity/field constraint violations are reported to users in addition to and prior to the lower-level field/property violation that is the root cause of the higher-level constraint failure; this could be seen as confusing - it is more helpful to focus the user's attention on the lower-level violation.
Here's a proposal for addressing this without an elaborate system of explicit dependencies between constraints:
1. Use try/catch around the call to each constraint validator, and add a violation "Could not verify '{$constraint_message'" if the validator blows up and throws an exception or error. This would mean that validators did not need to be coded defensively; they could generally assume lower-level data was valid.
2. Only add this new kind of violation, if the child node validators do not add any violations. This would lead to the user typically only being exposed to a single violation message that correctly identified the root cause. The only drawback to this is that a poorly coded validator, which breaks not because lower-level data is invalid but because of some other unrelated bug, will not have its failure surfaced to the user until after any other lower-level violations are fixed. I think that's acceptable - bugs cause suboptimal UX by nature, we can't prevent that.
3. Store/retrieve/display the violations in the opposite sequence to now. If we think it's often better UX for the lower-level violations to be reported first and acted on first, then we can just handle that either at the level of the violation internal storage, retrieval, or even display. We don't need to change the sequence of execution to address this.
Comment #85
wim leersThe problem we're trying to solve here is two-fold:
\Drupal\Core\TypedData\Validation\RecursiveContextualValidator::validateNode()) recursive validation strategy, which results in the entity-level validation constraints not being able to assume valid field values and field-level validation constraints not being able to assume valid property valuesI suspect
are part of the solution.
\Drupal\Tests\jsonapi\Functional\ResourceTestBasehas thorough test coverage, but it does NOT assert that the validation error messages in case of a nonsensical value (for example, the stringhello worldbeing specified for a boolean "status" field or an integer "weight" field).That test (and the REST equivalents) are specifically testing access control and basic shape of the representation, not detailed validation.
I have recently started encountering this problem again, but now in the world of Config, because I've been working on config validation. There, it's likely that you'll see multiple errors for the same config property path, for example:
It goes without saying that the latter (generated by the
NotNullconstraint) should result in none of the other validators running, because there literally is no point in them running. The consequence: noisy, confusing validation errors.Comment #86
mxr576Wow, wonderful thread! :) I learned a lot from reading this and I am also still unsure how many problems we have and what are those... :D
I also encountered the problem in the past that in validators you have to use defensive coding or you have to "fail silently" in Validator A because Validator B will fail too and that is enough.
I have checked the EmailValidator from Symfony 7.1 and they also use defensive coding there.
Maybe we should just forget stacking independent validator units and instead concrete and complex requirements should be covered by a specific validator... which goes against composability and (again) goes against what we can see in Symfony... BUT as it was highlighted and proved with historical context in #77, we already have a Drupalism in typed data validation due to the implementation does not support validation groups (which are essentially a solution for considering a set of independent validators as one validator unit).
Comment #87
mxr576Well there is https://symfony.com/doc/7.1/reference/constraints/Compound.html for that in Symfony, but that would still expose all validation errors independently and maybe what I would expected a "composite" would have its own dedicated validation error that would be displayed when any of the validators failed.
Again... preferences and use cases.
Comment #88
bbralaKinda like what the AnyOf of symfony does. Have 2 places where something similar is coming:
#3432353: Add validation constraints to core.extension
ValidSequenceKeysConstraintValidator
And #3461720: Create AtLeastOneOf constraint.
Kinda like the compound validation errors there. We did need a added __cone method there, but seems a resonable way to do multiple.