Postponed on #2343035: Upgrade validator integration for Symfony versions 2.5+, see #46.

Motivation

For #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay we need to solve conditional field validation, i.e. validation value of field A in dependence of the value field B. #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay requires that we have knowledge about all involved fields A and B.
As #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay is critical, bumping this to critical as well.

Proposed resolution

Add a new base class for such constraints and call it CompositeConstraintBase.
Ensure covered fields are specified for that constraints and test that information is available from violations.
Convert our single use-case in core CommentNameConstraintValidator to the new base class.

Remaining tasks

Get #2343035: Upgrade validator integration for Symfony versions 2.5+ done.
Implement it.

User interface changes

-

API additions

  • Constrains/violations which act on multiple fields can now be defined using Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase
  • There constrains can specify via the coversFields() method to which fields they apply to
  • Those constraints have to be added on the entity level
  • In case the a validator uses addViolation() with either $invalidValue, $plural or $code,
    for compatibility with symfony 3.0 and above violations should now be added using the following code:
              $context->buildViolation('Please enter a number between %min% and %max%.')
                  ->setParameter('%min%', 3)
                  ->setParameter('%max%', 10)
                  ->addViolation();
    

Original report by fago:
Quoting from #2015687-46: Convert field type to FieldType plugin for taxonomy module which describes a needed feature for entity validation:

2). A conditional field. For example, a field contains two elements, when user chooses 'pre-defined' for first element, we need to validate the other element for options list. When user chooses 'Custom', we don't need to validate the pre-defined option list.

Right now, you can do that by
a) writing your own constraint plugin or
b) use the Callback constraint + a custom validation callback instead
b) dynamically providing your own constraints from within getConstraints().
c) Symfony suggests using validation groups: http://symfony.com/doc/current/book/validation.html#group-sequence-provi...

Let's figure out what the pos/cons are on this topic and document what's the best approach.
Related symfony PR (that got reverted) - "dynamic constraints".

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

fago’s picture

tagging

fago’s picture

Issue summary: View changes

b) dynamically providing your own constraints from within getConstraints().

As discussed with webmozart, dynamically defined constraints do not really make sense, and do not allow us to benefit from having upfront defined constraints any more, e.g. adding a way to translate constraints to JS based constraint validation.

larowlan’s picture

Priority: Normal » Critical

This blocks criticals

webchick’s picture

Hm, can you clarify? #2278523: Avoid dynamically defining constraints (in related issues) is only a major. And the only other issue it references is #2023465: Allow validation constraints to add in message replacements which is also a major.

catch’s picture

larowlan’s picture

larowlan’s picture

Assigned: Unassigned » larowlan

optimistically taking this on, if you don't see anything in a week or so, send a search party, I'll be trapped in typed data hell

larowlan’s picture

Assigned: larowlan » Unassigned
Priority: Critical » Normal

From what I can see - this might already be done - the ConstraintValidator classes are passes a FieldItemList as arguments, which means the entity ($items->getEntity()) is available, meaning although a validator is defined for a single field, it has access to all field values.
I don't think this blocks #2403485: Complete conversion of comment form validation to entity validation anymore, so going to demote this and unpostpone that. See you over there.

dawehner’s picture

It turns out that you want to validate more than the stuff defined by your entity type, but you want to be able to apply business logic on your validation as well.
For that usecase we need a way for this, see #2405503: How to validate a REST submitted node?

larowlan’s picture

dawehner’s picture

@larowlan
Note: I don't think that its the best idea to pin that kind of validation onto base fields directly. We are talking about things you normally just do for some bundles in a custom module so having an api which scales horizontal would be nice.

fago’s picture

From what I can see - this might already be done - the ConstraintValidator classes are passes a FieldItemList as arguments, which means the entity ($items->getEntity()) is available, meaning although a validator is defined for a single field, it has access to all field values.

Yes, it already is easily possible to do it somehow, but we need to do it in a secure way, such that entity forms do not wrongly ignore / not-run relevant validation logic. See #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay for more about the problems around what to to validate and mapping/ignore violations. Howsoever, yeah one can definitely already start converting the validtion logic into constraints and put it on the field where the violation would show up. Then, the remaining bit would be "only" making it safe.

We are talking about things you normally just do for some bundles in a custom module so having an api which scales horizontal would be nice.

You can add the constraints via hook_entity_base_field_info_alter().

fago’s picture

Title: Figure out the best way to do conditional validation » Add CompositeConstraintBase for conditional validation involving multiple fields
Issue summary: View changes

Figuring out this is required for solving #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay. Updated the issue summary accordingly and added a proposed resolution.

While discussing this with webmozart, webmozart suggested using the term "CompositeConstraint". I like it as ConditionalConstraint isn't descriptive at all, every constraint is a conditional. Composite seems to fit as it will check multiple things.

fago’s picture

Priority: Normal » Critical
larowlan’s picture

Assigned: Unassigned » larowlan

why not

larowlan’s picture

So I think we can use something in a similar fashion to http://symfony.com/doc/current/reference/constraints/Collection.html here, but we need #2429037: Allow adding entity level constraints first to be able to add the composite constraint to the comment entity and replace CommentNameConstraintValidator with something similar - so I think that then makes this postponed as well - but will work on a patch that also includes #2429037: Allow adding entity level constraints as it stands.

larowlan’s picture

Assigned: larowlan » Unassigned

my brain can't process this at the moment

fago’s picture

I don't think Collection buys us anything here - we already have the ComplexData constraint which basically is our variant of the symfony Collection.

dawehner’s picture

FileSize
7.24 KB

I'm curious how horrible wrong this is.

larowlan’s picture

and now my brain can process it @dawehner++ code > text

dawehner’s picture

Status: Active » Needs review
Issue tags: +Needs tests

Let's see whether the testbot likes us

Status: Needs review » Needs work

The last submitted patch, 19: 2105797-19.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
7.48 KB
1.77 KB

This time with more manual testing.

dawehner’s picture

FileSize
11.36 KB
6.65 KB

And some documentation / test coverage.

larowlan’s picture

I have two questions about how you would work with a FieldItem instead of the scalar values in case you need access to more than just the main property. And what happens if you want to validate the second or third item in the field item list (or access the whole FieldItemList) ?

I personally think that passing the field name in fields should get you the field item list. The validator should deal with whether it wants the first/second/each value or the required properties - also if you know you want a specific property, we could also support [fields => [field_foo.0.value]] style format.

Eg in summary

  • ['fields' = ['field_foo']]; // Gets you the FieldItemList.
  • ['fields' = ['field_foo.0']]; // Gets you the FieldItem at delta 0.
  • ['fields' = ['field_foo.0.title']]; // Gets you the scalar value of the title property of delta 0.
  1. +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CompositeConstraintValidatorBase.php
    @@ -0,0 +1,66 @@
    +      $split = explode('.', $field);
    

    We should add the third parameter here, but as mentioned above we should allow up to three (name, delta, property)

  2. +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CompositeConstraintValidatorBase.php
    @@ -0,0 +1,66 @@
    +      $values[$field] = $entity->{$field}->first()->{$property};
    

    I think wiring this to first() is wrong

  3. +++ b/core/tests/Drupal/Tests/Core/Validation/Plugin/Validation/Constraint/CompositeConstraintValidatorBaseTest.php
    @@ -0,0 +1,83 @@
    +  public function testValidate() {
    

    can we get a docblock and a @covers here?

fago’s picture

  1. +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CompositeConstraintBase.php
    @@ -0,0 +1,43 @@
    +  protected $fields = [];
    

    Can we just make an abstract method, such that one is required to implement it?

  2. +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CompositeConstraintBase.php
    @@ -0,0 +1,43 @@
    +  public function getFields() {
    

    Not sure there is value in providing a helper method like that, why would i use that instead of directly accessing the fields from $entity ?

fago’s picture

Not sure there is value in providing a helper method like that, why would i use that instead of directly accessing the fields from $entity ?

well, the value I see is that it helps to ensure people are only using the fields they are supposed to. But for that, having an array of field item objects would suffice as well, e.g.:


$fields = $this->getFields();
if ($fields['name']->value == 'Name') {
 // ...
}

But still, you can use the regular API and there we can save the possibly unnecessary preprocessing work?

dawehner’s picture

well, the value I see is that it helps to ensure people are only using the fields they are supposed to. But for that, having an array of field item objects would suffice as well, e.g.:

If you don't specify the fields you want from outside, what exactly is the major gain of the base class? Can't you pull all the values you want
already from HEAD? I try to figure out where exactly the value would be, if you still have to do it manually?

larowlan’s picture

Can't you pull all the values you want
already from HEAD? I try to figure out where exactly the value would be, if you still have to do it manually?

Yes, this issue made no sense to me, see #15, #16, #17 - until I saw it in code from @dawehner. If you remove the getFields, then I'm back at the 'makes no sense to me' station

larowlan’s picture

dawehner: is this just about defining the properties in the annotation so that form validation can show errors online?

larowlan: dawehner: oh, you might be right, so entityformdisplay will scan the constraints and see oh, constraint x needs field a and b, but a isn't in this form, so no need to validate

larowlan: dawehner: in which case I think it makes sense to keep getFields, but just make them return the item list?

dawehner: larowlan: right
dawehner: larowlan: maybe this explains a bit what fago was saying
larowlan’s picture

+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/CompositeConstraintBase.php
@@ -0,0 +1,43 @@
+abstract class CompositeConstraintBase extends Constraint {

I was thinking about how this applies to the blocked issue on entity-form displays and how the code might look if this were in. In my opinion if we had an interface for composite constraints the code would be nicer because we'd get if ($constraint instanceof CompositeConstraintInterface) instead of using is_subclass_of()

Also, if what @dawehner said via irc in #30 is correct, do we need to be using 'name' in 'fields' in the conversion of CommentNameAuthorConstraint (sp?) if name is the field it is attached to already? Should we just be tracking fields that the constraint depends on in addition to the field it is attached to?

jibran queued 24: 2105797-24.patch for re-testing.

fago’s picture

dawehner: is this just about defining the properties in the annotation so that form validation can show errors online?

quite so - it's such that #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay can do its job for composite constraints and add it in as soon as such one of the involved fields is shown on the form. If that was not clear from the issue summary, please help clarifying it!

In my opinion if we had an interface for composite constraints the code would be nicer because we'd get if ($constraint instanceof CompositeConstraintInterface) instead of using is_subclass_of()

I don't see the relationship, instanceof can be used with classes also? As constraints are value objects and do not have interfaces in symfony either, I'd rather not add one?

  • I worked on this a bit, see attached patch:
  • I moved the constraint below \Entity, as it's targets entity fields.
  • I figured we'll need #2429037: Allow adding entity level constraints for this such that we can move this to the entity level also.
  • Moved to an abstract coversFields() method. But shouldn't it rather be an annotation? We can still throw an exception in the validator if the annotation is not defined?

Problem still is how to handle access to the entity best. People should not use other field values, but as the comment name validator shows there are cases where you need access to the entity still. That's fine as long as it's not used to access other un-covered fields. As it's always possible somehow to do $items->getEntity()->other_field I guess we can only solve this by documentation.

dawehner’s picture

I honestly still don't see how the entity display validation should be aware, of which fields are covered. Does that mean its code has to special case the new base constraint?

larowlan’s picture

So thinking ahead to #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay we're going to collect entity-level constraints in the form display, and filter out those that don't apply, then apply the remaining.
As I understand it, the validate method on the entity doesn't accept an array of constraints, how are we going to pass that context to the validator?
I can't fault the code as it stands, but I'm not sure it gets us towards the end-solution just yet.

fago’s picture

Does that mean its code has to special case the new base constraint?

yes, that's the only way it's going to work. There is nothing existing we can build upon, we have to add that. Symfony validator does not support multiple property paths covered for a constraint.

I can't fault the code as it stands, but I'm not sure it gets us towards the end-solution just yet.

We can filter the violations once they are created, based upon their constraint, property path (if not composite) and field access.

larowlan’s picture

Status: Needs review » Reviewed & tested by the community

We can filter the violations once they are created, based upon their constraint, property path (if not composite) and field access

Perfect, that makes total sense now, thank you

dawehner’s picture

Status: Reviewed & tested by the community » Needs work
FileSize
16.52 KB
12.64 KB
  • Removed the validator base, its not worth it, IMHO
  • Tried to integrate it into EntityFormDisplay, but it seems to be impossible to get the constraint from a given violation.
    \Symfony\Component\Validator\ExecutionContext::addViolation does not add the constraint, \Symfony\Component\Validator\Context\ExecutionContext::addViolationdoesn't do it, so we maybe have to wait for #2343035: Upgrade validator integration for Symfony versions 2.5+
fago’s picture

hm, I was thinking to add the violation filtering logic in #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay and just make sure the implementation of composite contraints done here provides the metadata necessary for it. #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay already prepares a patch for it, so could we work on it there? I'd be happy about reviews about the approach taken there!

\Symfony\Component\Validator\ExecutionContext::addViolation does not add the constraint, \Symfony\Component\Validator\Context\ExecutionContext::addViolation

ouch, good finding :-/

fago’s picture

So what it already adds is the violation code, so instead we could use the violation code for now. That would work, but requires the validator developer to take care of that, what is quite bad for DX. So looks like #2343035: Upgrade validator integration for Symfony versions 2.5+ is indeed a dependency for filtering those violations :(

Removed the validator base, its not worth it, IMHO

As we have to continue to pass on $entity to the validator, I agree - there is no benefit left.

dawehner’s picture

@fago
So should we better postpone onto #2343035: Upgrade validator integration for Symfony versions 2.5+ and then finish this issue?

fago’s picture

Not sure, if possible I'd love to avoid that to not unnecessarily stall things - but if we do not find another way that seems to be the logical consequence.

effulgentsia’s picture

Title: Add CompositeConstraintBase for conditional validation involving multiple fields » Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, are executed in all of the necessary situations
Issue tags: +Triaged D8 critical

Discussed with the branch maintainers and they agreed this is critical, given that core has such a constraint, making this a hard blocker for #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay. Tagging, retitling, and see also #2395831-54: Entity forms skip validation of fields that are not in the EntityFormDisplay.

effulgentsia’s picture

Issue tags: +blocker

Also adding the blocker tag, since another issue is postponed on this.

effulgentsia’s picture

Issue tags: +Security, +WSCCI

Also tagging for security and WSCCI, as it is required for secure web services.

fago’s picture

Title: Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, are executed in all of the necessary situations » [PP1] Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered

Actually, making sure composite constraints will be executed in all necessary situations won't work before #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay gets done as well (which depends on this issue), thus re-titling to the issue scope of just discovering composite constraints and the fields covered.

However, as figured out in comments #38-#42 we need to get #2343035: Upgrade validator integration for Symfony versions 2.5+ done before in order to be able to know the constraint which caused a violation. The only way I see to not postpone this now would be adding an interim solution based on certain a violation code which the validator would have to specify when adding a violation. But that would be an burden for validator implementers + an API change when that burden gets removed later on.
Thus, in order to avoid those changes I think it's best to postpone this issue on #2343035: Upgrade validator integration for Symfony versions 2.5+.

fago’s picture

Issue summary: View changes
effulgentsia’s picture

Status: Needs work » Postponed

Per #46.

effulgentsia’s picture

Title: [PP1] Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered » [PP-1] Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered
Crell’s picture

There's a massive amount of context here that I don't have... what exactly is needed from the WSCCI side?

effulgentsia’s picture

I don't think there's anything needed here from the WSCCI team, but I tagged it WSCCI in #45 due to there being potential security problems (data validations not executed) in non-form REST submissions until this is fixed.

jibran’s picture

Status: Postponed » Needs work
jibran’s picture

Title: [PP-1] Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered » Add CompositeConstraintBase so that constraints involving multiple fields, such as CommentNameConstraint, can be discovered
dawehner’s picture

Status: Needs work » Needs review
FileSize
14.07 KB
2.44 KB

hm, I was thinking to add the violation filtering logic in #2395831: [PP-2] Entity forms skip validation of fields that are not in the EntityFormDisplay and just make sure the implementation of composite contraints done here provides the metadata necessary for it. #2395831: [PP-2] Entity forms skip validation of fields that are not in the EntityFormDisplay already prepares a patch for it, so could we work on it there? I'd be happy about reviews about the approach taken there!

Oh sure, let's do that there . This is for now just a reroll.

Status: Needs review » Needs work

The last submitted patch, 54: 2105797-54.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
10.1 KB
3.97 KB

This test doesn't make sense anymore now that we don't longer have the base class ...

Given that fago also wanted to introduce the actual form integration in #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay it doesn't really make sense to test something related to this here.
I think its fine to keep EntityTestCompositeConstraint as it can be used in the future.
Beside from that there is nothing to do here, honestly.

larowlan’s picture

Yep, we just needed the 2.5 API so we could get the constraint from the violation. Which we have.

In my books, this is RTBC except for:

+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php
@@ -0,0 +1,32 @@
+ * type. See ... @todo. fix this.

This would have referenced #2429037: Allow adding entity level constraints but that is since fixed. So the See ... @todo can instead be @see \Drupal\Core\Entity\EntityType::addConstraint or a reference to the annotation.

dawehner’s picture

Issue tags: -Needs tests
FileSize
10.14 KB
933 bytes

There we go.

larowlan’s picture

Status: Needs review » Reviewed & tested by the community

unless bot disagrees, but possibly even if bot disagrees (bots have been misbehaving today)

amateescu’s picture

The patch looks good to me as well, I just found two small nitpicks:

  1. +++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php
    @@ -46,40 +47,35 @@ public static function create(ContainerInterface $container) {
    +    $owner_id = (int) $entity->uid->target_id;
    

    Is this really necessary? Wouldn't it prevent someone from overriding the User entity class and use "machine names" for the id? Sure, they would need to keep '0' as the machine name for the anonymous user but other than that it should be possible :)

  2. +++ b/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
    @@ -55,6 +55,7 @@ public function testValidation() {
    +    $this->assertEqual($errors['type'], 'Multiple fields are validated', 'Constraint violation is generated correctly');
    

    If you're going to address the point above (i.e. if you roll a new patch), how about also changing the message here to "Composite constraint violation is generated correctly"?

larowlan’s picture

(int) $entity->uid->target_id
This is consistent with EntityOwnerInterface

yched’s picture

+++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php
@@ -0,0 +1,34 @@
+ * With that the $values in CompositeConstraintValidatorBase::doValidate() will
+ * return an array keyed with 'name', 'uid' and 'entity', with 'entity' being
+ * the actual full entity.

The "name, uid, entity" part looks specific to the CommentNameConstraint concrete class ?

Also, is that really up to date ? Implementations in the patch seem to be doing $entity = $value->getEntity() ?

fago’s picture

Status: Reviewed & tested by the community » Needs work

Yes, yched is right - the constraints are not defined at the right level (any more?).

  1. +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php
    @@ -0,0 +1,34 @@
    + * The constraint must be defined on entity-level; i.e., added to the entity
    + * type.
    

    Exactly.

  2. +++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php
    @@ -46,40 +47,35 @@ public static function create(ContainerInterface $container) {
    +    $entity = $value->getEntity();
    

    So this constraint needs to be moved also.

  3. +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraintViolation.php
    @@ -44,6 +44,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    +    $fields['type']->addConstraint('EntityTestCompositeConstraint', array());
    

    This test coverage is bogus as the constraint is supposed to be added on entity level.

fago’s picture

Assigned: Unassigned » fago

I'll take a look.

The last submitted patch, 58: 2105797-58.patch, failed testing.

The last submitted patch, 38: 2105797-37.patch, failed testing.

fago’s picture

Status: Needs work » Needs review
FileSize
11.27 KB
7.71 KB

Adapted the constraints accordingly so they operate on the right level.

There are still some things left:
- We need to add test coverage for the test constraint and test we can detect composite constraint violations
- It would be good to ensure composite constraints are put on entity level.
- Generally, I wonder whether we should stop using ->context->addViolation() in favour of the new 2.5 buildViolation() API. Should be a separate issue though.

I'll continue later on the first two points.

dawehner’s picture

  1. +++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php
    @@ -21,6 +20,13 @@
    +   * Validator 2.5 and upwards compatible execution context.
    +   *
    

    I don't think we have to explicit document that its compatible with 2.5 and above ... the interface already has that kind of information.

  2. +++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php
    @@ -62,21 +62,27 @@ public function validate($value, Constraint $constraint) {
    -        $this->context->addViolation($constraint->messageNameTaken, array('%name' => $author_name));
    +        $this->context->buildViolation($constraint->messageNameTaken, array('%name' => $author_name))
    +          ->atPath('name')
    +          ->addViolation();
    ...
    -        $this->context->addViolation($constraint->messageMatch);
    +        $this->context->buildViolation($constraint->messageMatch)
    +          ->atPath('name')
    +          ->addViolation();
    

    Urgs, so we now have to use that new API which is a little bit more verbose?

fago’s picture

I don't think we have to explicit document that its compatible with 2.5 and above ... the interface already has that kind of information.

I now, I just wanted to point out that's the reason for redefining the variable here. It's already defined in the parent - but not with the 2.5 interface. Maybe, we should just re-define a Drupal base validator class which has that? Seems useful for everyone that needs to use buildViolation().

Urgs, so we now have to use that new API which is a little bit more verbose?

Yes, for every call that wants to pass anything else than message + replacements. However, IDEs mark every call to addViolation() as deprecated as they do not know it's only about the added parameters.

fago’s picture

Assigned: fago » Unassigned
FileSize
11.55 KB
4.61 KB

So attached patch adds test coverage that ensures we can determine which constraints are composite. That way we know the covered fields and #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay is able to filter constraints accordingly - or not.

- It would be good to ensure composite constraints are put on entity level.

Not sure how we'd do that best. It doesn't fit into the validation integration as that operates on Typed Data level, so maybe best it would just go into a composite constraint validator base class? Still, no one would enforce you are using that but I guess it should be enough to cover typical accidential errors?

jibran’s picture

Status: Needs review » Needs work
Issue tags: +Needs issue summary update, +Needs change record

This is an api addition so API change section needs an update in IS and we need a change notice as well.

  1. +++ b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php
    @@ -0,0 +1,34 @@
    + * With that the $values in CompositeConstraintValidatorBase::doValidate() will
    + * return an array keyed with 'name', 'uid' and 'entity', with 'entity' being
    + * the actual full entity.
    
    +++ b/core/modules/comment/src/Entity/Comment.php
    +++ b/core/modules/comment/src/Entity/Comment.php
    @@ -54,6 +54,9 @@
    

    This doesn't make sense. There is no class name CompositeConstraintValidatorBase neither in this patch nor in core.

  2. +++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php
    @@ -46,41 +53,36 @@ public static function create(ContainerInterface $container) {
    -        $this->context->addViolation($constraint->messageNameTaken, array('%name' => $author_name));
    +        $this->context->buildViolation($constraint->messageNameTaken, array('%name' => $author_name))
    +          ->atPath('name')
    +          ->addViolation();
    

    I know we need a change notice overall but we need to mention this in it as well.

The last submitted patch, 67: d8_composite.patch, failed testing.

The last submitted patch, 70: d8_composite.patch, failed testing.

dawehner’s picture

Issue summary: View changes

Added an IS/CR

dawehner’s picture

Status: Needs work » Needs review
FileSize
13.05 KB
3.19 KB

This doesn't make sense. There is no class name CompositeConstraintValidatorBase neither in this patch nor in core.

Good catch! This is now entirely not needed anymore.

I hope the fix for the CommentAnoymousTest is now the proper way to do, before we have the custom violation list introduced in #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay

Status: Needs review » Needs work

The last submitted patch, 75: 2105797-75.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
FileSize
14.08 KB
2.28 KB

There we go, this time with the right bug fix.

jibran’s picture

Thanks for the fixes.

+++ b/core/modules/comment/src/CommentForm.php
@@ -323,9 +324,12 @@ public function validate(array $form, FormStateInterface $form_state) {
+      if ($violation->getPropertyPath() === 'name') {

Why not ditch this and use $violation->getPropertyPath() as a first param of the setErrorByName Method?

dawehner’s picture

Why not ditch this and use $violation->getPropertyPath() as a first param of the setErrorByName Method?

Well, we just want to take into account violations against the name field here, other violations, like for fields itself should be ignored here.
They are handled in the entity display form component.

Yes this means that at the moment, we run most of the validations twice for comments.

fago’s picture

  1. +++ b/core/modules/system/src/Tests/Entity/EntityValidationTest.php
    @@ -168,4 +172,26 @@ protected function checkValidation($entity_type) {
    +    $this->assertTrue($constraint instanceof CompositeConstraintBase, 'Constraint is composite contraint.');
    

    Checking my on test, I think we should add test coverage for the violation being added on the right sub-path also.

  2. +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestConstraints.php
    @@ -26,7 +26,7 @@
    - *     "NotNull" = {}
    + *     "NotNull" = {},
    

    Great this works now, but this change is now useless.

ad buildViolation():

I know we need a change notice overall but we need to mention this in it as well.

The change is only required if you pass any additional arguments beside message + message params. But that's easy missed when addViolation() is used else - thus I'm wondering whether it would make sense to promote using buildViolation() always, or at least always for composite contraints? It's a little bit more verbose, but less error-prone. In particular composite constraints will have to add sub-paths.

fago’s picture

Yes this means that at the moment, we run most of the validations twice for comments.

Right. We could prevent it by validating only entity-level constraints manually - but given that's only there until #2395831: Entity forms skip validation of fields that are not in the EntityFormDisplay is fixed I do not care.

dawehner’s picture

Good points, addressed them quickly.

jibran’s picture

Status: Needs review » Reviewed & tested by the community

The change is only required if you pass any additional arguments beside message + message params. But that's easy missed when addViolation() is used else - thus I'm wondering whether it would make sense to promote using buildViolation() always, or at least always for composite constraints? It's a little bit more verbose, but less error-prone. In particular composite constraints will have to add sub-paths.

I think only in composite constraints is fine.

+++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraint.php
@@ -7,17 +7,18 @@
-use Symfony\Component\Validator\Constraint;
+use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;

Nice.

I think everything is done here.

fago’s picture

Thanks - I agree this is ready to go.

I took another look at the CR draft and improved it a little. The buildViolation() change mentioned there has actually been introduced by https://www.drupal.org/node/2343035 - we missed it there as there was no place we had to convert in core so far. Thus, I removed it from this CR draft and updated https://www.drupal.org/node/2480711 instead to mention this change.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

This issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 69ce355 and pushed to 8.0.x. Thanks!

diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php
index 1436f7c..12f5bb8 100644
--- a/core/modules/comment/src/CommentForm.php
+++ b/core/modules/comment/src/CommentForm.php
@@ -19,7 +19,6 @@
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\Validator\ConstraintViolation;
 
 /**
  * Base for controller for comment forms.
diff --git a/core/modules/system/src/Tests/Entity/EntityValidationTest.php b/core/modules/system/src/Tests/Entity/EntityValidationTest.php
index 1cce032..d4358fc 100644
--- a/core/modules/system/src/Tests/Entity/EntityValidationTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityValidationTest.php
@@ -188,7 +188,7 @@ public function testCompositeConstraintValidation() {
 
     // Make sure we can determine this is composite constraint.
     $constraint = $violations[0]->getConstraint();
-    $this->assertTrue($constraint instanceof CompositeConstraintBase, 'Constraint is composite contraint.');
+    $this->assertTrue($constraint instanceof CompositeConstraintBase, 'Constraint is composite constraint.');
     $this->assertEqual('type', $violations[0]->getPropertyPath());
 
     /** @var CompositeConstraintBase $constraint */

Fixed on commit.

  • alexpott committed 69ce355 on 8.0.x
    Issue #2105797 by dawehner, fago, larowlan, effulgentsia, jibran,...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.