From 32801642fc1bc9f4a9942ce90e9b3669e74d16b3 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Tue, 5 Jul 2016 09:59:22 +0100
Subject: [PATCH] Issue #1266748 by swentel, Berdir, jcnventura,
 arlinsandbulte, yched, droplet, amateescu: Changing cardinality lower than
 highest existing delta causes data loss upon save

---
 .../src/Form/FieldStorageConfigEditForm.php   | 17 ++++++++
 .../field_ui/src/Tests/ManageFieldsTest.php   | 39 +++++++++++++++++++
 2 files changed, 56 insertions(+)

diff --git a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
index 1f4f94a850d4..bbaeb63322d9 100644
--- a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
@@ -148,10 +148,27 @@ protected function actions(array $form, FormStateInterface $form_state) {
   public function validateForm(array &$form, FormStateInterface $form_state) {
     parent::validateForm($form, $form_state);
 
+    $field_storage_definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($this->entity->getTargetEntityTypeId());
+
     // Validate field cardinality.
     if ($form_state->getValue('cardinality') === 'number' && !$form_state->getValue('cardinality_number')) {
       $form_state->setErrorByName('cardinality_number', $this->t('Number of values is required.'));
     }
+    // If a specific cardinality is used, validate that there are no entities
+    // with a higher delta.
+    elseif (!$this->entity->isNew() && isset($field_storage_definitions[$this->entity->getName()]) && $form_state->getValue('cardinality') != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
+
+      // Get a count of entities that have a value in a delta higher than the
+      // one selected. Deltas start with 0, so the selected value does not
+      // need to be incremented.
+      $entities_with_higher_delta = \Drupal::entityQuery($this->entity->getTargetEntityTypeId())
+        ->condition($this->entity->getName() . '.%delta', $form_state->getValue('cardinality'))
+        ->count()
+        ->execute();
+      if ($entities_with_higher_delta) {
+        $form_state->setErrorByName('cardinality_number', $this->formatPlural($entities_with_higher_delta, 'There is @count entity with @delta or more values in this field.', 'There are @count entities with @delta or more values in this field.', ['@delta' => $form_state->getValue('cardinality') + 1]));
+      }
+    }
   }
 
   /**
diff --git a/core/modules/field_ui/src/Tests/ManageFieldsTest.php b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
index 673b7319e4b1..cbe9f53e9f55 100644
--- a/core/modules/field_ui/src/Tests/ManageFieldsTest.php
+++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
@@ -267,6 +267,23 @@ function cardinalitySettings() {
     $this->assertLink(t('Field settings'));
     $this->assertLinkByHref($field_edit_path);
 
+    // Add two entries in the body.
+    $edit = ['title[0][value]' => 'Cardinality', 'body[0][value]' => 'Body 1', 'body[1][value]' => 'Body 2'];
+    $this->drupalPostForm('node/add/article', $edit, 'Save');
+
+    // Assert that you can't set the cardinality to a lower number than the
+    // highest delta of this field.
+    $edit = [
+      'cardinality' => 'number',
+      'cardinality_number' => 1,
+    ];
+    $this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
+    $this->assertRaw(t('There is @count entity with @delta or more values in this field.', ['@count' => 1, '@delta' => 2]), 'Correctly failed to set cardinality lower than highest delta.');
+
+    // Create a second entity with three values.
+    $edit = ['title[0][value]' => 'Cardinality 3', 'body[0][value]' => 'Body 1', 'body[1][value]' => 'Body 2', 'body[2][value]' => 'Body 3'];
+    $this->drupalPostForm('node/add/article', $edit, 'Save');
+
     // Set to unlimited.
     $edit = array(
       'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
@@ -276,6 +293,28 @@ function cardinalitySettings() {
     $this->drupalGet($field_edit_path);
     $this->assertFieldByXPath("//select[@name='cardinality']", FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
     $this->assertFieldByXPath("//input[@name='cardinality_number']", 1);
+
+    // Assert that you can't set the cardinality to a lower number then the
+    // highest delta of this field but can set it to the same.
+    $edit = [
+      'cardinality' => 'number',
+      'cardinality_number' => 1,
+    ];
+    $this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
+    $this->assertRaw(t('There are @count entities with @delta or more values in this field.', ['@count' => 2, '@delta' => 2]), 'Correctly failed to set cardinality lower than highest delta.');
+
+    $edit = [
+      'cardinality' => 'number',
+      'cardinality_number' => 2,
+    ];
+    $this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
+    $this->assertRaw(t('There is @count entity with @delta or more values in this field.', ['@count' => 1, '@delta' => 3]), 'Correctly failed to set cardinality lower than highest delta.');
+
+    $edit = [
+      'cardinality' => 'number',
+      'cardinality_number' => 3,
+    ];
+    $this->drupalPostForm($field_edit_path, $edit, t('Save field settings'));
   }
 
   /**
-- 
GitLab