From 66aaf44b798afd7e9bf402eeb770ca1a5b4cdced Mon Sep 17 00:00:00 2001
From: Lee Rowlands <lee.rowlands@previousnext.com.au>
Date: Tue, 27 Jun 2023 06:59:30 +1000
Subject: [PATCH] Issue #3126654 by idimopoulos, smustgrave, borisson_,
 mglaman, pfrenssen, catch: EntityConstraintViolationList::findByCodes is
 inconsistent

---
 .../Entity/EntityConstraintViolationList.php  | 14 +++++++++++
 ...EntityConstraintViolationListInterface.php | 14 +++++++++++
 .../EntityConstraintViolationListTest.php     | 24 +++++++++++++++----
 3 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/core/lib/Drupal/Core/Entity/EntityConstraintViolationList.php b/core/lib/Drupal/Core/Entity/EntityConstraintViolationList.php
index 0eda78891b3e..d644e66d3c0a 100644
--- a/core/lib/Drupal/Core/Entity/EntityConstraintViolationList.php
+++ b/core/lib/Drupal/Core/Entity/EntityConstraintViolationList.php
@@ -170,6 +170,20 @@ public function filterByFieldAccess(AccountInterface $account = NULL) {
     return $this->filterByFields($filtered_fields);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function findByCodes(string|array $codes): static {
+    $violations = [];
+    foreach ($this as $violation) {
+      if (in_array($violation->getCode(), $codes, TRUE)) {
+        $violations[] = $violation;
+      }
+    }
+
+    return new static($this->getEntity(), $violations);
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/lib/Drupal/Core/Entity/EntityConstraintViolationListInterface.php b/core/lib/Drupal/Core/Entity/EntityConstraintViolationListInterface.php
index 273a64f689c3..c344e2a29792 100644
--- a/core/lib/Drupal/Core/Entity/EntityConstraintViolationListInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityConstraintViolationListInterface.php
@@ -47,6 +47,20 @@ public function getByField($field_name);
    */
   public function getByFields(array $field_names);
 
+  /**
+   * Filters this violation list by the given error codes.
+   *
+   * Copied from Symfony parent class
+   * \Symfony\Component\Validator\ConstraintViolationList.
+   *
+   * @param string|string[] $codes
+   *   The codes to find.
+   *
+   * @return \Drupal\Core\Entity\EntityConstraintViolationListInterface
+   *   A list of violations of the given fields.
+   */
+  public function findByCodes(string|array $codes): static;
+
   /**
    * Filters this violation list by the given fields.
    *
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityConstraintViolationListTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityConstraintViolationListTest.php
index cb427f6a20a6..ba2c227701d9 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityConstraintViolationListTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityConstraintViolationListTest.php
@@ -75,6 +75,22 @@ public function testFilterByFieldAccessWithCompositeConstraint() {
     $this->assertEquals(array_values(iterator_to_array($constraint_list)), [$violations[2], $violations[3], $violations[4], $violations[5]]);
   }
 
+  /**
+   * @covers ::findByCodes
+   */
+  public function testFindByCodes() {
+    $account = $this->prophesize('\Drupal\Core\Session\AccountInterface')->reveal();
+    $entity = $this->setupEntity($account);
+
+    $constraint_list = $this->setupConstraintListWithoutCompositeConstraint($entity);
+    $violations = iterator_to_array($constraint_list);
+
+    $codes = ['test-code-violation-name', 'test-code-violation2-name'];
+    $actual = $constraint_list->findByCodes($codes);
+    $this->assertCount(2, $actual);
+    $this->assertEquals(iterator_to_array($actual), $violations);
+  }
+
   /**
    * Builds the entity.
    *
@@ -121,11 +137,11 @@ protected function setupConstraintListWithoutCompositeConstraint(FieldableEntity
     $violations = [];
 
     // Add two violations to two specific fields.
-    $violations[] = new ConstraintViolation('test name violation', '', [], '', 'name', 'invalid');
-    $violations[] = new ConstraintViolation('test name violation2', '', [], '', 'name', 'invalid');
+    $violations[] = new ConstraintViolation('test name violation', '', [], '', 'name', 'invalid', NULL, 'test-code-violation-name');
+    $violations[] = new ConstraintViolation('test name violation2', '', [], '', 'name', 'invalid', NULL, 'test-code-violation2-name');
 
-    $violations[] = new ConstraintViolation('test type violation', '', [], '', 'type', 'invalid');
-    $violations[] = new ConstraintViolation('test type violation2', '', [], '', 'type', 'invalid');
+    $violations[] = new ConstraintViolation('test type violation', '', [], '', 'type', 'invalid', NULL, 'test-code-violation-type');
+    $violations[] = new ConstraintViolation('test type violation2', '', [], '', 'type', 'invalid', NULL, 'test-code-violation2-type');
 
     // Add two entity level specific violations.
     $violations[] = new ConstraintViolation('test entity violation', '', [], '', '', 'invalid');
-- 
GitLab