Problem/Motivation

Closer look at validator

/**
   * {@inheritdoc}
   */
  public function validate($items, Constraint $constraint) {
    if (!$item = $items->first()) {
      return;
    }
    $field_name = $items->getFieldDefinition()->getName();
    /** @var \Drupal\Core\Entity\EntityInterface $entity */
    $entity = $items->getEntity();
    $entity_type_id = $entity->getEntityTypeId();
    $id_key = $entity->getEntityType()->getKey('id');

    $value_taken = (bool) \Drupal::entityQuery($entity_type_id)
      // The id could be NULL, so we cast it to 0 in that case.
      ->condition($id_key, (int) $items->getEntity()->id(), '<>')
      ->condition($field_name, $item->value)
      ->range(0, 1)
      ->count()
      ->execute();

    if ($value_taken) {
      $this->context->addViolation($constraint->message, [
        '%value' => $item->value,
        '@entity_type' => $entity->getEntityType()->getLowercaseLabel(),
        '@field_name' => mb_strtolower($items->getFieldDefinition()->getLabel()),
      ]);
    }
  }

reviled that only first field item is check for duplicates.

Proposed resolution

Add test coverage and fix validator.

Remaining tasks

  1. Write test to confirm bug
  2. Write test/s to cover all field types that doesn't have a field specific "unique" constraints/validators (see related issues)
  3. Review
  4. Commit

User interface changes

None

API changes

None

Data model changes

None

Comments

RoSk0 created an issue. See original summary.

rosk0’s picture

Assigned: Unassigned » rosk0
rosk0’s picture

Status: Active » Needs review
StatusFileSize
new6.12 KB

Test only patch.

Status: Needs review » Needs work

The last submitted patch, 3: 2999225-3-test-only.patch, failed testing. View results

rosk0’s picture

Status: Needs work » Needs review

Appropriate test.
Float fields validation still doesn't work and I don't understand why...

rosk0’s picture

StatusFileSize
new7.36 KB

Forgotten patch.

Status: Needs review » Needs work

The last submitted patch, 6: 2999225-5-test-only.patch, failed testing. View results

rosk0’s picture

Assigned: rosk0 » Unassigned
Status: Needs work » Needs review
StatusFileSize
new7.93 KB
new6.9 KB

Spent couple hours reading floats documentation to remember/understand them and why test wasn't working as I expected. Basically it doesn't make any sense to expect this constraint being applied to float because they are so tricky and

testing floating point values for equality is problematic, due to the way that they are represented internally

I removed them from test. Any kind of uniqueness/validation for float fields will require custom constraint with corresponding business logic.

Bug still there of the rest of fields types: email, telephone, datetime, list_integer, list_string, timestamp, text, string, decimal, integer. So provided patch has test for them and fixed validator.

Expected fail in test-only patch.

Status: Needs review » Needs work

The last submitted patch, 8: 2999225-9-test-only.patch, failed testing. View results

wengerk’s picture

Status: Needs work » Needs review
Related issues: +#2478663: UniqueFieldValueValidator works only with single value fields

Add #2478663: UniqueFieldValueValidator works only with single value fields as related issue.
It may seems to be a duplicate nop ?

I see that #2478663: UniqueFieldValueValidator works only with single value fields doesn't have a working patch, what should we do ?

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

alphawebgroup’s picture

Status: Needs review » Needs work

Hi Guys
the latest 2999225-9.patch is failed to apply

rosk0’s picture

Status: Needs work » Needs review
StatusFileSize
new6.9 KB
new7.72 KB

Re-roll.

I will will probably update referenced in #10 issue with the patch from here and close this one as the other is older and has more people looking at it.

Let's make sure that patch is still working first.

The last submitted patch, 13: 2999225-13-test-only.patch, failed testing. View results

berdir’s picture

  1. +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php
    @@ -32,8 +32,13 @@ public function validate($items, Constraint $constraint) {
    +    $values = [];
    +    foreach ($items as $item) {
    +      $values[] = $item->value;
    +    }
    +
         $value_taken = (bool) $query
    -      ->condition($field_name, $item->value)
    +      ->condition($field_name, $values, 'IN')
           ->range(0, 1)
    

    Technically hardcoding ->value is wrong as you could for example use this on an entity reference field. But that was already wrong before and is a separate problem.

  2. +++ b/core/tests/Drupal/KernelTests/Core/TypedData/UniqueFieldValueValidatorTest.php
    @@ -0,0 +1,250 @@
    +
    +  /**
    +   * Data provider.
    +   */
    +  public function validationDataProvider() {
    +    $dateTime = new \DateTime();
    

    not a huge fan of data providers on kernel tests. While they are much faster than functional tests, there's still a bit of overhead. Could also be done with just a regular loop over the data returned by this, not sure.

cilefen’s picture

Title: Unique field constraint does not validated field values on delta greater then 0 » Unique field constraint does not validated field values on delta greater than 0

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

andyf’s picture

StatusFileSize
new8.59 KB
new1.43 KB

Thanks, this seems to be working for me!

Technically hardcoding ->value is wrong as you could for example use this on an entity reference field. But that was already wrong before and is a separate problem.

I don't want to divert the issue but was wondering if it makes sense to add a method for grabbing the value just in anticipation of solving the problem for different field types, eg. ER, elsewhere - please see attached.

andyf’s picture

StatusFileSize
new8.59 KB

(Exactly the same patch as #18 rolled for D8.7.)

joachim’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php
@@ -32,19 +33,37 @@ public function validate($items, Constraint $constraint) {
+  protected function getItemValue(FieldItemInterface $item) {
+    return $item->value;
+  }

> if it makes sense to add a method for grabbing the value just in anticipation of solving the problem for different field types, eg. ER, elsewhere - please see attached.

I don't think the approach in the patch is the right one. The patch is adding a method to the validator. But we're not going to subclass the validator for different field types are we? That would be a pain.

Isn't mainPropertyName() what we should use here?

berdir’s picture

> mainPropertyName()

Yes. But as I wrote, I'd be fine to improve that in a follow-up issue, it's existing problem with the current code.

andyf’s picture

Status: Needs work » Needs review

Sorry all, I think I have diverted the issue: I'll hide the patches I added. @joachim IIUC you're suggesting I do something along the lines of this?

protected function getItemValue(FieldItemInterface $item) {
  $property_name = $item->getDataDefinition()->getMainPropertyName();
  return $item->{$property_name};
}

Putting back to needs review for #13.

Thanks!

joachim’s picture

> @joachim IIUC you're suggesting I do something along the lines of this?

Yes, though I agree with @Berdir that it's best done in a follow-up, to keep this issue about just one problem.

(Sorry, I should have said that in my earlier comment!)

andyf’s picture

it's best done in a follow-up, to keep this issue about just one problem.

Thanks, understood, I kinda knew I was breakin the rules but foolishly thought my change would be small and correct! My specific use case is a multi-value ER field, so I've added a little comment to #2973455: Unique field constraint does not work with any field main property name that is not "value".

Thanks!

andyf’s picture

Status: Needs review » Needs work

At the moment the validator's still passing $item->value as part of the violation message.

Also at least the following constraints' messages are worded for a single value, don't know if that's something to be addressed as part of this issue?

  • FeedTitleConstraint
  • FeedUrlConstraint
  • FileUriUnique
  • UniqueFieldConstraint
  • UserMailUnique
  • UserNameUnique

I guess any error message we give for a multi-value field will be (annoyingly?) vague or require more queries or entity loading?

Thanks

mbovan’s picture

+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php
@@ -32,19 +33,37 @@ public function validate($items, Constraint $constraint) {
-      ->condition($field_name, $item->value)
+      ->condition($field_name, $values, 'IN')

In my opinion #2478663: UniqueFieldValueValidator works only with single value fields provides a more comprehensive fix considering #2478663-56... The tests from this issue would be very useful.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

caesius’s picture

caesius’s picture

Status: Needs work » Closed (outdated)