Problem/Motivation
Might be blocking #2105797: Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered.
#2278353: Update to Symfony 2.5 is going with the 2.4 Symfony validation integration API. Because HEAD is now on Symfony 2.5, we should upgrade the validation API integration.
Proposed resolution
Use the 2.5 API validation.
We are achieving this by implementing the validator interfaces in our own code. This makes the code much easier to follow for the case
of validating an entity / typed_data and also works around problems in implementing the newer metadata API.
Remaining tasks
None
User interface changes
None.
API changes
- All validation calls must use the 2.5 style.
- Type hints to the 2.4 interface \Symfony\Component\Validator\ValidatorInterface must be updated to use the 2.5 interface \Symfony\Component\Validator\Validator\ValidatorInterface
- Constraints on fields can be called with empty item objects now, instead of being called with NULL.
Symfony Validator API usage
+ ConstraintViolationBuilderInterface
+ ExecutionContextFactoryInterface
+ ContextualValidatorInterface
+ ConstraintViolation
+ ConstraintViolationList
+ PropertyPath
Classes, of which we created our own variant because they are internal in Symfony:
ConstraintViolationBuilder
ExecutionContextFactory
According to the Symfony BC promise this non-internal usages should be fine. Worst thing that can happen is a added method to one of the interfaces, which would be noted in the uprade notes.
| Comment | File | Size | Author |
|---|---|---|---|
| #216 | validator-2.5-2343035-5.215.patch | 106.67 KB | larowlan |
| #215 | upgrade_validator-2343035-215.core-only.do-not-test.patch | 83.11 KB | larowlan |
| #215 | interdiff.txt | 1009 bytes | larowlan |
| #214 | 2343035-214.patch | 106.66 KB | dawehner |
| #214 | interdifff-with-update.txt | 33.71 KB | dawehner |
Comments
Comment #1
cilefen commentedComment #2
hussainwebIt is unlikely that it will disappear in 2.x, at least. Symfony 2.3 onwards uses semver, so backward compatibility will be there. But let's do this anyway. I think this is related to #2343043: valid_email_address() should use egulias/EmailValidator and become deprecated.
I am putting it back to Active since #2278353: Update to Symfony 2.5 has gone in.
Comment #3
webmozart commentedI can confirm that the BC mode won't disappear in the 2.x line. Symfony 3.0, however, won't feature that mode.
Is there anything I can do to help?
Comment #4
cilefen commentedComment #5
cilefen commented@webmozart: If Drupal 8 does not ever go to SF 3, then we need not do anything. I have heard the plan is to get to 2.7 and stay there for a while.
Comment #6
webmozart commented@cilefen: That's correct. However, no new features will be added to the 2.4 API. Just be aware of that.
Comment #7
cilefen commented@webmozart So we may want to proceed with this issue. The goal would be to change the API level, which is set in TypedDataManager, and upgrade all the validation calls.
Comment #8
cilefen commented@webmozart So we may want to proceed with this issue. The goal would be to change the API level, which is set in TypedDataManager, and upgrade all the validation calls.
Comment #9
cilefen commentedThis is merely the API version change. I expect there will be test failures.
Comment #10
cilefen commented@webmozart Is it the case that if we do not call setApiVersion() then the API decision is automatic? It seems obvious.
Comment #12
webmozart commented@cilefen If you do not set a version, the API version will switch between 2.4 (for PHP < 5.3.9) and 2.5-BC (for PHP > 5.3.9).
The 2.5-BC API uses classes implementing multiple interfaces (2.4+2.5) with overlapping method definitions. This wasn't supported in PHP < 5.3.9. Hence you need to set the version to 2.5 manually if you want to use the 2.5 API also on PHP < 5.3.9.
Comment #13
cilefen commented@webmozart Thank you for the explanation. Drupal 8 requires 5.4+. Considering that, this a choice between doing nothing and remaining on 2.5-BC or going to 2.5. In your opinion, is that a good idea in terms of new features?
Comment #14
webmozart commented@cilefen Well:
Since Drupal 8 is not released yet, the BC argument (pro 2.5-BC API) should not really apply. The performance difference (pro 2.5 API) should be minimal, but you better measure it for yourselves. So you can take any of them, really.
Comment #15
cilefen commentedThis is with the 2.5 backwards-compatible API.
Comment #16
cilefen commentedComment #17
cilefen commentedComment #19
Miroling commentedComment #20
Miroling commentedComment #21
Miroling commentedComment #22
Miroling commentedNeed automatic patch testing
Comment #24
Miroling commentedNeed to fix tests after upgrade_validator-2343035-17.patch
BTW
I recommend starting resolve this issue from FieldValidationTest
Comment #25
cilefen commented@Miroling: Thanks for working on this issue. Some tips: name the patches after the comment number. https://dreditor.org/ helps you find the next patch filename. Interdiffs are appreciated https://www.drupal.org/documentation/git/interdiff.
Comment #26
Miroling commented@cilefen Ok, Thank you for suggestions :)
Comment #27
webmozart commentedCan I do anything to help? If you want, we can have a phone call to resolve your issues.
Comment #28
cilefen commented@webmozart: Thank you for following up on this issue. I am asking around on IRC whether this change is too disruptive now that D8 is in beta.
Comment #29
catchAll external library updates should be treated as critical during the beta, otherwise we have very little chance of being able to use or contribute back upstream bug fixes and performance issues once 8.0.0 is out.
I'm also hoping we can update at least some libraries in minor versions assuming no major bc breaks, and that absolutely depends on not using old/deprecated versions of things beforehand.
Comment #30
cilefen commented@webmozart: The task is to get the tests passing with the patch in #21. Now that this issue is critical it will receive more attention.
Comment #31
cilefen commentedDrupal\Core\TypedData\Validation\Metadata uses the deprecated Symfony\Component\Validator\PropertyMetadataInterface also.
Comment #33
hussainwebI am giving this a shot. There are many interfaces changed, as mentioned in #32. The factory interface has also changed. I do not have an interdiff as I went about this separately. This is mainly because the patch has all method implementations in
ClassMetadataand none inMetadata. I thought it would be simpler to just derive what I needed and start fresh.I have given some defaults to cascading and traversal strategy. I will need some help there. I will be pretty surprised if the patch passes, but it's a start.
Comment #34
webmozart commented@hussainweb If you need help, contact me at bschussek@gmail.com. I'm the developer of the Validator component.
Comment #36
hussainwebAnother attempt. I have no reason to do this but I am curious to see if installer moves ahead, at least, when there is no exception.
Comment #37
hussainwebThank you for the offer to help @webmozart. I am trying to understand this more. I have sent you an email with more details.
Comment #39
dawehnerThank you for your work @hussainweb
As far as I understand the BC layer will exist until symfony 3.0, which we won't use for Drupal 8 at least, given the changes.
We will probably either stick with 2.7 or even follow the 2.x releases over time.
Given that I wonder whether this issue should be critical. Yes its super annoying
that we use the old API, but it doesn't feel like a real critical.
Anyone wants to disagree?
Comment #40
cilefen commenteddawehner I agree with you.
Comment #43
martin107 commentedRetest - just wanted to see what was what after #2389287: Missing PhpExecutableFinder
Comment #44
ianthomas_ukThis should at least allow Drupal to install, albeit with user_validate_name disabled for now.
Comment #45
ianthomas_ukOr it might if I upload my new patch.
My changes, as I didn't get an interdiff:
I've commented this out as I couldn't work out how to fix it and it was preventing installation. Obviously we'll need a proper fix before committing.
Both branches were looking for ComplexDataInterface
Comment #48
ianthomas_ukI'm making progress, I've commented the bits I'm currently stuck on with 'help wanted' in the patch. I've been using the edit user for to test for now.
Comment #50
ianthomas_ukI'm making progress, but there are two things I don't understand:
1) Why RecursiveContextualValidator is insisting getMetadataFor() returns a ClassMetadataInterface, when we sometimes want to return a PropertyMetadataInterface. I've asked for help on that at https://github.com/symfony/symfony/issues/11393
2) Why UserNameConstraintValidator is getting $items as an array, when it was written to expect an object (I assume TypedData\ListInterface).
Comment #52
ianthomas_ukI can get some validators to work by changing Metadata::getPropertyValue() to return $this->typedData instead of $this->typedData->getValue(), but doing so breaks others that are expecting other types (PrimitiveTypeConstraintValidator, Symfony's EmailValidator).
There's also a problem in EntityChangedConstraintValidator, because $this->context->getMetadata() returns a Symfony GenericMetadata, when our test needs a drupal Validation\Metadata.
Comment #53
catchWhether this is critical depends on the answer to #2395443: [policy, no patch] Follow symfony 2.7 or 3.0., this is the question. Unless we make a commitment to never use the 3.x branch of Symfony with 8.x, I think it probably should be though.
Comment #54
yched commented@catch : I might be missing something, but I don't get how this is tied to the decision about Symfony 3 ?
This issue is about using Validation 2.5.
Also, apparently it would let us solve #2023465: Allow validation constraints to add in message replacements
Comment #55
ianthomas_uk@yched The old, 2.4, API will not be supported in 3.x
Comment #56
berdir@yched: It is related because that is when they will remove the old API/BC layer that we are currently using.
Comment #57
catchComment #58
catchAdding #2400407: [meta] Ensure vendor (PHP) libraries are on latest stable release as a parent issue. This isn't strictly a vendor library upgrade but it is a blocker for future ones.
Comment #59
catchComment #60
chx commentedAs an alternative consider removing the validator entirely. To call the DX bad is offensive to bad things. pwolanin tried to fix it and failed so I recommend admitting failure and postpone to D9. Reminder: the DX is putting a plugin id in
->setConstraints(array(and then putting the validation logic into a magic named class, the plugin class plus the Validator postfix. Due to the declarative nature of the definition it is almost impossible to figure this out.Comment #61
dawehner@chx
To be fair, you would have to admit that you need to replace it with a system which works for the complex structure of entities/typed data
at the same time. The effort to rewrite it probably requires as much effort.
Comment #62
chx commentedSymfony supports typed data...? *blink*
Edit: what I am trying to say, make the plugins standalone somehow. pwolanin tried to keep Symfony and move the validator class inside the plugin class and failed so I am recommending nuking Symfony and using our own logic to fire validation and thus keeping the plugins standalone. My favorite tune is this: the Drupal-Symfony integration is horrid and we need either fix it or where we can't fix it we should postpone adding Symfony.
Comment #63
bojanz commentedCan we set it to Validation::API_VERSION_2_5_BC instead?
AFAIK, it won't change anything in the patch, but would still allow older validators to still be used.
That would increase our interoperability, because there's no way libraries will be switching exclusively to the new API (most still support symfony 2.3 because it's LTS, but even when 2.7 comes out there will be no pressure for them to move off the old API).
So, the argument here is interoperability with other libraries contrib authors might want to pull in.
Comment #64
ianthomas_uk@chx we already are manually triggering the validation, which makes this harder to debug because it's triggered in different places, for example TypedData::validate() and ComplexDataConstraintValidator::validate(). /@chx
Interestingly the latter call passes $constraints as a parameter to the symfony function RecursiveContextualValidator::validate(), which causes symfony to create a new symfony GenericMetadata instance instead of a drupal Metadata or ClassMetadata instance.
GenericMetadata has a more complicated CascadingStrategy and TraversalStrategy than our classes. It looks like this is how we should be stopping the excessive recursion I mentioned in my patch, but I wasn't able to get it working yet and it's late.
Comment #65
dawehner@ianthomas_uk
One of the hunks I made locally for experimental reasons is the following: This makes the unique validator a bit more generic.
Comment #66
fagoI tried to get this working with Validation::API_VERSION_2_5 (once that works we can still opt to do Validation::API_VERSION_2_5-BC even), but I failed.
I ran into following problems:
Not a problem per se, but there is no generic property container metadata interface any more. Now, each object is automatically validated via validateObject() which assumes classmetadata. We do not want to validate any typical class properties, so this is not a good fit for us. Still, our objects are objects that need validation so that could be ok if it works out.
The main problem I ran into is that validator assumes that each class property is a "generic node", i.e. it does not detect properties that are objects - but that's critical for us to have. We cannot use implicit traversal as we need to preprocess the value validated as we can do it via getPropertyValue() (see below also).
While for class properties, we could massage the value independently of the metadata object (see MetadataBase::getPropertyValue()), we cannot do so for the initial object that's passed to the validator for validation. We should be work around this by preprocessing things before invoking symfony validator and passing a simple, scalar value and its contraints only if a non-container is to be validated - not nice, but that would probably work.
Maybe this can still be implemented using the cascading feature, I did not wrap my head around this yet but it from reading its docs it sounds like it shouldn't be necessary to use.
Udated (not working) patch attached.
Comment #68
fagoFigured cascading is exactly what we need / I missed before. So we do explicit traversable and cascading, no implicit traversable. -> Got the tree traversal working that way.
Still one problem with the following left:
This has issues with the primitive type constraint type validator as it cannot access the typed data object for validation any more. We could solve this by splitting the constraint validation up into multiple constraints and adding in the right constraint upfront.
Updated patch attached, not fully working yet.
Comment #69
fagoAttaching right patch file, interdiff was correct.
Comment #72
cilefen commentedReroll.
Comment #73
andyceo commentedComment #75
dawehnerWhile debugging this it seemed to be that the validation got applied to two levels, not sure whether its FieldItemList and FIeldItem or FieldItem and the typed data.
Comment #76
dawehner... Things broke a bit more in the meantime.
It seems to be that there is no way to get back to the typed data inside the ConstraintValidation ... one way around it seems to be that explicitly allowing to set the typed data on the constraint and access that via the Validation ... It is really odd that we are now not longer possible to set our own metadata.
Comment #77
dawehnerSee some experiments in the interdiff.
Comment #78
fagoDidn't I just post here? Thanks d.o. for eating my (rather long) comment :/
I did discuss this a little with webmozart while talking about #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay. Traversable and cascading are in general both fine, but traversable does not allow us to process the property value before validation. So we need to use cascading and turn implicit traversing off in order to avoid double-validation. That way we can leverage getPropertyValue() of the class metadata to process the values before validation + keep the extra-check for primitives when invoking validation. So far this seems like it should work.
Generally, we'll have to use ClassMetadata for all containers, and can use some property metadata for the others. So containers are "classes" and non-containers "properties" for the validator.
Yeah, I was thinking about that as well. At least a helper method getTypedData() would probably be helpful.
#77 looks quite ugly though: Shouldn't it still work with $this->context->getObject()? From the docs it looks like it should give us the next parent object?
Comment #79
larowlanplanning to work on this
Comment #80
cilefen commentedTo those just getting oriented on this issue, here is where the different validator is selected:
The validate() functions in each one take different arguments.
Comment #81
cilefen commentedhttps://github.com/symfony/symfony/blob/master/UPGRADE-3.0.md#user-conte...
Comment #82
Anonymous (not verified) commentedlooking at this during DrupalSouth 2015.
current status - using the patch at 72 and the interdiff at 77, i get install failures during install.
Symfony\Component\Validator\Exception\InvalidOptionsException: The option "typedData" does not exist in constraint Drupal\user\Plugin\Validation\Constraint\UserNameConstraint in Symfony\Component\Validator\Constraint->__set() (line 195 of core/vendor/symfony/validator/Symfony/Component/Validator/Constraint.php). Symfony\Component\Validator\Constraint->__set('typedData', Object) Drupal\Core\TypedData\TypedData->getConstraints() Drupal\Core\Field\FieldItemList->getConstraints() Drupal\Core\TypedData\Validation\MetadataBase->findConstraints('Default') Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateInGroup(Object, '000000007890501e00007f2df4327681', Object, 'Default', Object) Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateClassNode(Object, '000000007890501e00007f2df4327681', Object, '', Array, NULL, 1, Object) Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateObject(Object, '', Array, 1, Object) Symfony\Component\Validator\Validator\RecursiveContextualValidator->validate(Object, NULL, NULL) Symfony\Component\Validator\Validator\RecursiveValidator->validate(Object) Drupal\Core\TypedData\TypedData->validate() user_validate_name('admin') Drupal\Core\Installer\Form\SiteConfigureForm->validateForm(Array, Object) call_user_func_array(Array, Array) Drupal\Core\Form\FormValidator->executeValidateHandlers(Array, Object) Drupal\Core\Form\FormValidator->doValidateForm(Array, Object, 'install_configure_form') Drupal\Core\Form\FormValidator->validateForm('install_configure_form', Array, Object) Drupal\Core\Form\FormBuilder->processForm('install_configure_form', Array, Object) Drupal\Core\Form\FormBuilder->buildForm('Drupal\Core\Installer\Form\SiteConfigureForm', Object) install_get_form('Drupal\Core\Installer\Form\SiteConfigureForm', Array) install_run_task(Array, Array) install_run_tasks(Array) install_drupal(Object)Comment #83
Anonymous (not verified) commentedjust want to see how this runs, got install working again with the help of @larowlan.
Comment #85
Anonymous (not verified) commentednot sure how to proceed on this.
fatals in the test are due to expectations in the new SF2 validators that the current code doesn't satisfy.
so, we're going to need to wrap SF2, or rewrite a bunch of code in the validators.
Drupal\Core\Validation\Constraint\AllowedValuesConstraint, for example, needs to have a public $choices or $callback in the new validator world. (it's kinda crappy that public variables are required, that's not much of an API.)
Comment #86
sidharrell commentedComment #87
sidharrell commentedlooks like this patch may conflict with #2346373: Data reference validation constraints are applied wrong
should one or the other be postponed blocked on the other?
Comment #88
larowlan@sidharrell, no we don't postpone on conflicts - but thanks for pointing it out.
I'm slowly making progress on this, will continue tonight after work.
Comment #89
larowlaninterdiff against #72
We're already passed $value, which is typed-data, not sure why we had to try and re-get it from context.
This creates duplicate messages, so will fail, but at least we're getting somewhere.
Un-assigning in case others want a look, but will prod some more tomorrow or perhaps later tonight.
Comment #90
larowlanAlso, injected the current user whilst at-it
Comment #92
larowlanSo $value isn't always typed data
Comment #93
dawehnerCurrently working on it, but I'm convinced that we need the following patch.
Now I'm looking at the double validation issue.
Comment #94
larowlanThis passes FeedValidationTest locally
Comment #95
larowlanmerged HEAD
Comment #96
larowlaninterdiff at #94 is against #89
Comment #97
larowlaninstalls locally√
Comment #98
larowlanStill getting fails on other validations locally, but progress
Comment #101
larowlanthis starting to get on the mental side, wrapping a property in an envelope with the constraints we want and the typed data we need.
I think we need to rethink how ComplexDataConstraintValidator works, replacing it with property metadata.
Comment #102
dawehnerThis is so funny, I had the exact same approach but I'm convinced that the interdiff in #93 is all we actually need.
Comment #104
dawehnerCreated an issue in symfony: https://github.com/symfony/symfony/issues/13885
Comment #105
larowlanActually, after sleeping on this - I think we can make PropertyContainerMetadata smart enough to recognise ComplexDataConstraint and serve the properties/constraints using getPropertyValue|getConstrainedProperties|hasPropertyMetadata|getPropertyMetadata i.e. we recognize ComplexDataConstraintValidator in PropertyContainerMetadata::{find|get}Constraints and register the properties/constraints internally in PropertyContainerMetadata and then remove ComplexDataConstraint from the list of constraints, and allow the RecursiveContextualValidator to validate them. When the RecursiveContextualValidator asks PropertyContainerMetadata for properties to validate (getConstrainedProperties) we can return the properties that were contained inside the ComplexDataConstraint constraint, then we can use hasPropertyMetadata|getPropertyMetadata to return the property constraints and getPropertyValue to handle the 'fetch the property value' logic that was in ComplexDataConstraintValidator. We can then remove ComplexDataConstraintValidator.
Comment #106
larowlanImplements idea in #105
Still needs work for traversal/cascade because we're getting duplicates - and some constraints no longer working, but I think step in the right direction - more tomorrow.
Comment #108
martin107 commentedJust providing information
Symfony 2.6.5 was released today
http://symfony.com/blog/symfony-2-6-5-released?utm_source=feedburner&utm...
Comment #109
yesct commented#2105797-41: Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered suggests this is blocking it
Comment #110
hussainweb@martin107: Thanks, I created another issue for the Symfony update: #2454393: Upgrade to Symfony 2.6.5.
Comment #111
larowlanMore work handling typed data correctly now, but still missing the secret sauce on travesal/cascade.
If we say 'cascade' on the entity, some tests pass, others don't.
Looking for some help here.
Comment #112
larowlanfull interdiff
Comment #114
larowlana broken patch is better than no patch
more tomorrow
Comment #118
fagoSee #78 in case you missed that.
It might help simplifying things a bit to customize the NotNull constraint to support typed data and check isEmpty() for complex data and lists. Then, we can save the headache of massing empty typed data to NULL values.
Comment #119
cilefen commentedTypedData class name changes: https://www.drupal.org/node/2457329
Comment #120
dawehnerSpend an evening trying to understand what is going on.
One problem I encountered is that we stop the cascading as we don't take into account the PrimitiveItem based constrains, when we determine the cascading.
I solved that by making the cascading determination recursive.
On top of that we miss to pass along the constraints ... sadly now we have double constraints again ...
Comment #122
dawehnerThis is the code which is executed for the two code paths which lead to the duplicate path:
First one:
We validate the entity object validateClassNode
-- we validate each property of the object 572: $metadata->getConstrainedProperties() => validateGenericNode // These are the item lists
validateGenericNode:
-- line 702 => validateInGroup $value being the $itemList
-- line 759 => validateObject: cascading for the item list
-- validateClassNode
-- line 562: => validateInGroup
So once we validate the item list as part of being a property of the entity and once by being an object for itself.
This though seemed to be take into account by the architecture by the following code in validateInGroup:
It caches the constraint calls and tries to not execute it multiple times.
... Let's have a look what $cacheKey is. For the first call its
8b50000000139a060ad:title, for the second call its8810000000139a060ad.Two different cache paths, once for the validation of the property of the entity, once, for the item list as its own object ...
With the following patch, we can at least get the same constraints object, by caching the metadata, and so the constraints by typed data object.
Comment #123
dawehnerSent webmozart a mail, to see whether he has some clue about what is going on here.
Comment #124
effulgentsia commentedSince this is blocking #2105797: Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered, tagging as "Entity validation"
Comment #125
dawehnerhttps://github.com/dawehner/symfony/tree/test-nested-objects is some first test, not yet done, to try to reproduce the bit mentioned in #122
on upstream code.
Comment #126
dawehnerIt turns out, rerolling after #2346373: Data reference validation constraints are applied wrong is a bit of work.
Comment #127
fagoComment #128
xjmComment #129
fagoNot done yet, but I've got cascading working without duplications. What's left is fixing the complex constraint validators. I think adding a trait for getTypedData() should work here.
Comment #131
fagoSo turns out the main problem left is allow validators to fetch the typed data object. Turns out, this seems impossible to do with symfony validator 2.5 API is there is no way to process the initial object passed to the validator before it's passed on to validators.
I tried doing an intermediate workaround as discussed with dawehner, but turns out that does not work out either. The workaround works with a custom constraint to wrap things, but as soon as that is used the generic metadata is used and cascading is skipped. That makes entity validation fail. For completeness, attached is the version of the patch with this failing approach.
So looks like the only option left to ensure getTypedData() continues to work is improving symfony validator. I'll can try that later this week, but unassigning for now.
Comment #133
fagoWorking on this.
Comment #134
fagook, so here is a patch taking the new approach of fixing things in symfony validator instead of hacking around it. Let's see how this goes. Changes to symfony validator need to be contributed to validator and added in via composer still.
This actually fixes a regression in validator which removes property paths with a leading zero ("0.value") and improves it to allow value preprocessing.
Comment #136
fagoAdded API changes:
Comment #137
cilefen commentedComment #138
fagoThis should fix test fails - which are due to above API changes.
Comment #140
jibranThis is very nice work. Once the patch is green can we create PR against symfony/Validator and add a todo here?
nit
Comment #141
fagoNot addressed #140 yet.
Comment #142
larowlanGreen!!!!!
Can you add a diff for vendor only? After spending many an evening with this patch and in the recursive validator, keen to learn what the secret sauce was.
fago+=1000
Comment #143
larowlan%s/on/onto ?
The changes to the symfony component look fairly non-controversial to me, great work @fago
Comment #144
larowlanfixes 140 and some other minor nits
Comment #145
fagothanks. I've posted PRs for our changes: https://github.com/symfony/symfony/pulls/fago and opened tickets:
https://github.com/symfony/symfony/issues/14394
https://github.com/symfony/symfony/issues/14396
I'll reroll the patch to use https://github.com/fago/validator for now.
Comment #146
dawehnerI had a look at the recent changes and yeah they are great!
Comment #147
fagoworked on adding in a interim validator of mine that adds the PRs. It exposes some remaining issues with old API usage.
Unassigning while not working on it.
Comment #149
dawehner@fago
What are the next steps here, do we have to wait on the upstream PRs or can someone help with those?
Comment #150
fagoI'll discuss them with webmozart today, to make sure we are on the same page of the upstream issues. As soon as that's the case I think we could move on here. If there is agreement on the general direction of upstream PRs I think we could even move on while they are still being worked on. But if things move fast anyway, it would be better to wait for them of course.
Comment #151
webchickI think it depends how much of a blocker this is on other work, and how quickly we think the upstream issues will be resolved. In the past when we needed fixes to e.g. Doctrine we temporarily pointed composer.json to a fork to get around this problem. However, given we're in beta and there are some actual sites on D8 at this stage, I'd prefer not to do that unless absolutely necessary.
Comment #152
fago@webchick: Thanks, makes sense.
Unfortunately, webmozart cannot make it today, so we rescheduled discussion for tomorrow afternoon instead. I'll report back afterwards.
Comment #153
fagoDiscussed with webmozart via skype/hangouts.
He considers https://github.com/symfony/symfony/pull/14395 as a simple bug fix (as we do), so this should not be an issue to solve. For https://github.com/symfony/symfony/issues/14396, we've been wondering whether it doesn't make sense to throw away Validator's traversal logic and replace at with our own as a whole - as I already discussed with dawehner. This would give us more flexibility and save us from needlessly sticking with Symfony validator's class based metadata concept what does not fit so well for our approach. We'll meet again in person tomorrow to explore this option further.
Comment #154
effulgentsia commentedIs this something that can be quickly prototyped in order to see what it might look like before deciding?
Comment #155
fagoI met with webmozart today and we discussed more details based upon which I started implementing this. It's WIP and considered prototype-code, but it should be pretty complete. It's completely untested, so making it actually work would be the next step.
I really like how this looks compared to the previous approach, as the code and required logic WAY simpler. Instead of mapping our structure to symfony metadata's structure we can just stick with our structure and benefit from our existing metadata and use a simple traversal logic. However, being able to do so requires taking over some (rather simple) classes to avoid API compat. problems with internal classes (e.g. ValidationConstraintBuilder).
The implemenation does not cover groups at all - as we do not need them and did not implement them before in our Metadata implementations either. This allows our RecurisveValidator to save quite some code over the symfony one. If we decide we want to add this feature later on, we'd have to add a respective implementation also.
Comment #156
fagoOf course, our recursive validator implementation would need test coverage also.
Comment #158
jibranLet's update the IS after #155
Comment #159
dawehnerI really like how we have now code which is actually comprehensible and followable, that itself is a huge win!
I love that kind of documentation :P
As probably said personally, I think not implementing groups at the moment is totally fine. Its a feature which we don't use really and could add later, if it really would be needed.
It would be great to document that this not only iterates over properties but also items of a
FieldItemList/ListInterfaceI'm curious whether we still need that projection now that we have a way simpler system? Does it still happen that we validate the system thing multiple times?
If I understand correctly, we no longer use those methods so should we just throw an exception here as well?
it seems to be that this statement is no longer valid?
I guess we could reduce the amount of changes here, all we need is the new trait, as far as I understand.
Comment #160
dawehnerWorking on some basic unit test for some of the new classes now.
Comment #161
dashaforbes commentedRemoved unsupported method setMetaDataFactory from /var/lib/drupaltestbot/sites/default/files/checkout/core/lib/Drupal/Core/TypedData/TypedDataManager.php on line 335
Comment #162
dawehner@dashaforbes
Please always post an interdiff, otherwise its hard to check what you changed.
Here is a patch which ignores the changes in #161 so far, but adds the promised unit test with some "minor" fixes, just to get the unit tests running.
Comment #165
larowlanApplies 161 to 162
Comment #167
fagook, worked over it to make it actually working. This should be pretty complete, only some docs need love and tests need to be improved of course. I made the new test basically working by make use of real type data objects - I do not think we should mock them but test it with real Typed Data objects. That works, I did not convert one test method though.
Comment #168
fagoThis works on typed data level, not entities and fields - thus we should document this using typed data terminology. Clarified that though.
The methods for it are in the context interface. I do not think it's really needed, but it won't hurt either.
I think it's fine to return the value - if someone would ask this metadata class it just says no thanks. No reason to throw an exception imho.
I removed the interface in favor of $this->context->getObject() - I just made the object of our value to be always the Type Data object what seems reasonable to me.
Yes, those changes were introduced somewhere above and I just kept it as they make sense. I'd be fine with moving those to another issue though.
Comment #170
fagoComment #172
fagoFixed two problems:
Comment #174
dashaforbes commentedThanks @dawehner
Comment #176
dawehnerI'm trying to fix the few remaining test failures this morning.
Comment #177
dawehnerThe remaining problems are all around the problem of constructing the right property path.
We have two problematic cases:
core/lib/Drupal/Core/Entity/Entity/EntityFormDisplay.php:230For that case, we want to start with an empty property path for the root level. So $item_list['0']['property'] should have 0.property as path.
\Drupal\Core\Validation\Plugin\Validation\Constraint\ComplexDataConstraintValidator. In that case we want to continue the current property path.The easiest solution for that problem is to introduce an option $root_call variable on the validate() method. I played also around with atPath(),
but for now I sticked with $root_call (more experiments later).
Comment #178
webmozart commentedSome additional information about property paths: Property paths generally differ between array access and property access:
0.propertycorresponds to the PHP code$obj->0->propertyor$obj->get0()->getProperty()[0][property]corresponds to the PHP code$obj['0']['property']You can mix and match the two, e.g.
[0].propertycorresponds to the PHP code$obj['0']->propertyor$obj['0']->getProperty()I recommend you to construct the property paths accordingly. If you don't, you might run into problems later that we ran into with Symfony:
0.property.If you don't follow the conventions now, you block the door for the above points (and maybe more) without breaking BC in future releases.
Comment #180
fago@dawehner: thanks
@webmozart: Thanks, I do not think it is a problem though as we have all metadata in place to do the traversal depending whether we deal with lists or complex data anyway - besides that it's always just a get() call for us. So we have no reason to put it into our property paths, what keeps applying them and mapping them to form elements simpler.
Comment #181
dawehnerHere is the interdiff, sorry for forgetting to upload it.
Comment #182
bojanz commentedAre you sure you want to rename parameters declared by a parent interface, and documented there? It immediately makes the docblock wrong.
Core has generally elected to leave camelCase for code that extends Symfony.
(Yes, it's inconsistent, but that's the core coding standard we need to change after D8 is released).
Comment #183
dawehnerFixed the remaining failure, not sure whether this is the perfect fix though.
Comment #184
dawehnerJust doing some basic review here.
Let's use FQCN
@fago
So are you fine with adding the $root_call change?
That change is certainly out of scope :)
Comment #185
dawehnerFixing a couple of those nitpicks.
Comment #186
fagoThanks, great to see it green again.
Why that? Was that necessary somehow? That does not make sense to me.
I hated doing so but didn't know better. If we don't have to do that, perfect.
Comment #187
dawehnerThat change was needed for
$typed_data here is primitive most of the time, see all the checks below in that file.
Comment #188
fagoStill, our primitives are Typed data - so was the problem only the test? Probably the interface should extend TypedDataInterface even.
Comment #189
dawehnerAh yeah the problem was the test.
Comment #190
dawehnerWorked on a basic change record, feel free to add more stuff to it / correct stuff.
Also updated the issue summary.
Comment #191
jibranIs this intentional?
Comment #192
dawehnerWell, the problem is that the classes are coming directly from symfony, but sadly, for some reason, they are marked as internal, so we can't safely use them.
Let's keep them in sync in order to have an easy life to sync those classes in the future.
Comment #193
fagoWhat about calling it $is_root ? And documentating it as "Whether the node is the root of the validation tree; i.e., the node where validation started."
minor: Could be formatted nicer.
Also, left is the following todo:
So we need to either implement and test this, or just throw an unsupported exception. I'm not sure what would be th use of this, maybe webmozart can clarify?
Then I think we miss test-coverage for:
- validateProperty()
- validatePropertyValue()
Finally, I'm wondering about the two left Symfony-ish construct() comments. As that code is rather trivial, I don'T think we'll have to update it with a future Symfony version instead of just improving it. Thus, I think we can just drupalize those two left comments also.
I went through the patch and fixed a few doc nits. Besides that and the above I think this is complete and so almost ready.
Also added the following overview to the summary:
Symfony Validator API usage
+
ConstraintViolationBuilderInterface+
ExecutionContextFactoryInterface+
ContextualValidatorInterface+
ConstraintViolation+
ConstraintViolationList+
PropertyPathClasses, of which we created our own variant because they are internal in Symfony:
ConstraintViolationBuilderExecutionContextFactoryAccording to the Symfony BC promise this non-internal usages should be fine. Worst thing that can happen is a added method to one of the interfaces, which would be noted in the uprade notes.
Comment #194
fagoIn general, yes I think the solution is fine. Just see above about a docu improvement suggestion.
Comment #195
dawehnerWhat about using $is_root_call?
\Drupal\Core\Render\RendererInterface::renderis using the same name.Sure why not.
Comment #196
fagoI see, fine to me!
Comment #197
dawehnervalidateProperty()andvalidatePropertyValue()and fixed the bug, I think.Comment #198
dawehnerNow I think this patch should be ready, right before comment #200
Comment #200
dawehnerForgot the interface file.
Comment #202
klausiwhy is there a hash # at the end of line?
should be "typed data" here, right?
why are we casting the empty string '' to (string) here, does no make sense? Did you want to cast $basePath instead?
Comment #203
dawehnerThank you for your review klausi!!
Really good point!
Comment #204
jibranYay!!! this is green. Some doc update suggestions we can move this to followup if these are too much. Fixed some nits as well.
This will not play well with api.d.o.
Can we please explain the differences from Symfony/ExecutionContext here as well? Can we add @see
\Drupal\Core\TypedData\Validation\ExecutionContextFactory::createContext()as well?I think we have our own
ExecutionContextFactorynow so this can be updated.Can we add @see
\Drupal\Core\TypedData\Validation\RecursiveValidator::startContext()and\Drupal\Core\TypedData\Validation\RecursiveValidator::inContext()here?Can we fix this @todo here or do we need an issue for this?
Can we add this kind of docs to class desc as well?
Can we please explain the difference here between this and
Symfony\RecursiveValidator?Can we put @see
\Drupal\Core\TypedData\Validation\RecursiveValidator::getMetadataFor()here?Can we please explain here what's the purpose of this class and why is it different from
GenericMetadataetc.?\Symfony\Component\Validator\Violation\ConstraintViolationBuilderin\Drupal\Core\TypedData\Validation\ConstraintViolationBuilder.\Symfony\Component\Validator\Context\ExecutionContextFactoryin\Drupal\Core\TypedData\Validation\ExecutionContextFactory.Comment #205
jibranThe upstream PR is merged https://github.com/symfony/symfony/commit/f6c77ff362d39df7c7418f940ac850....
Comment #206
dawehnerWork on the great feedback from jibran and take care about the upstream change.
Comment #207
klausiAnd we are still casting the empty string here.
Yeah, this should be fixed. $this->defaultPropertyPath is not documented as property on the class. We should remove this line and add a comment like "Ignore any call to this method".
Missing fullstop on both lines.
Why do you need the $class variable here? You can just use the class name directly?
Comment #208
klausiCan we split this into 2 lines? I hate assignments in if() clauses.
You can remove the first line, @coversDefaultClass already tells me that.
Why \Exception and not the specific exception we throw there? Please add a comment.
Otherwise this looks pretty good!
Comment #209
dawehnerSure, here is one #2482527: Figure out whether we want to support \Drupal\Core\TypedData\Validation\RecursiveContextualValidator::atPath
I think that is kinda pointless, ... its a metadata implementation specific for type data, the generic metadata is entirely different.
Well sure, we can do that. Let's see how blocks the RTBC for that later :P
Skipped that part for now, as https://packagist.org/packages/symfony/validator does not yet have the more recent commit hash, grrr, so much time, no win.
But don't we use that pattern in gazilions of places in core? I don't dislike this pattern.
Comment #210
fagoNot sure. The classes are rather trivial, but as soon as we do that we need to note they are copied from Symfony what means we'll have to take over the copyright mention also. So imo either that, or provide our own version in Drupal style.
Comment #211
jibran@dawehner Thank you for appreciating my review. :)
I'm fine #210. Either Drupal cs or Symfony cs. I don't think mixture of both is a good thing. I think this is ready we can make a non-critical follow up for upstream fix.
mmethod. :)
Comment #212
dawehnerYeah, well, for now this is a version of the patch with some ignoring of the codestyle, see https://pear.php.net/manual/en/package.php.php-codesniffer.advanced-usag...
Comment #213
alexpottSpelling mistake - exceptions
So it ExecutionContext...
setNode is marked as internal
isGroupValidated is marked as internal
markGroupAsValidated is marked as internal
Some params need docs
isConstraintValidated is marked as internal
markConstraintAsValidated is internal
setConstraint is internal
Not used.
Does not exist and therefore not used.
With using methods and classes marked as internal I think we might just need to add a comment to explain why.
Comment #214
dawehnerThank you for the review.
Note: This patch now also updates symfony validator and ensures that we use the new property path from symfony itself again.
Can you clarify what you means with that?
This is fine, let me quote the @internal tag on the interface
\Symfony\Component\Validator\Context\ExecutionContextInterface::setNode:We are the validator engine ... and we implement exactly because of this reason the execution context interface ...
Fixed that
Comment #215
larowlanMinor tweak to the wording but rtbc+1.
I now have 5 local branches for this issue, new record and time for a cleanup
Comment #216
larowlanwrong file
Comment #217
jibranI think spelling mistake mentioned in #211 is still pending.
Comment #219
dawehner@jibran
I think its not, see
Comment #220
jibranSorry @dawehner I think I missed that in interdiff. I think feedback from @alexpott is addressed. Let's get this fixed.
Comment #221
ianthomas_ukI guess he meant "So *is* ExecutionContext". It does seem a little odd that sometimes it is OK for us to use things marked as internal and sometimes it isn't, but maybe the difference would be obvious if I read the patch more closely.
Comment #222
alexpottI wondered if we should add
@internalto our classes - but this would start a waterfall of annotations so leaving alone for now.Committed 373d925 and pushed to 8.0.x. Thanks!
Fixed some file perms on commit.
Comment #224
jibranPublished the change notice. Thank you @alexpott for committing this. Un-postponed the #2105797: Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered.
Thank you everyone especially @dawehner, @fago, @larowlan and @webmozart.
Comment #225
webmozart commentedGlad to see this fixed :)
Comment #226
fagoGreat to see this one land, thank you everyone!