From 2ceafd06b7d2080cbafc9b99ce2f2669770154a2 Mon Sep 17 00:00:00 2001 From: Lauri Eskola <lauri.eskola@acquia.com> Date: Sat, 28 Oct 2023 17:01:40 +0300 Subject: [PATCH] Issue #3052663 by joaopauloc.dev, smustgrave, quietone, Balu Ertl, xjm, scottsperry: Validate the min, max and default values for Numeric fields --- .../Field/FieldType/NumericItemBase.php | 29 ++++++++ .../src/Functional/Number/NumberFieldTest.php | 11 ++++ .../src/Kernel/Number/NumberItemTest.php | 66 +++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php index d1f3d644dbcb..1e4fbbb2b6da 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/NumericItemBase.php @@ -33,6 +33,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { '#type' => 'number', '#title' => $this->t('Minimum'), '#default_value' => $settings['min'], + '#element_validate' => [[static::class, 'validateMinAndMaxConfig']], '#description' => $this->t('The minimum value that should be allowed in this field. Leave blank for no minimum.'), ]; $element['max'] = [ @@ -124,4 +125,32 @@ protected static function truncateDecimal($decimal, $num) { return floor($decimal * pow(10, $num)) / pow(10, $num); } + /** + * Validates that the minimum value is less than the maximum. + * + * @param array[] $element + * The numeric element to be validated. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param array[] $complete_form + * The complete form structure. + */ + public static function validateMinAndMaxConfig(array &$element, FormStateInterface &$form_state, array &$complete_form): void { + $settingsValue = $form_state->getValue('settings'); + + // Ensure that the minimum and maximum are numeric. + $minValue = is_numeric($settingsValue['min']) ? (float) $settingsValue['min'] : NULL; + $maxValue = is_numeric($settingsValue['max']) ? (float) $settingsValue['max'] : NULL; + + // Only proceed with validation if both values are numeric. + if ($minValue === NULL || $maxValue === NULL) { + return; + } + + if ($minValue > $maxValue) { + $form_state->setError($element, t('The minimum value must be less than or equal to %max.', ['%max' => $maxValue])); + return; + } + } + } diff --git a/core/modules/field/tests/src/Functional/Number/NumberFieldTest.php b/core/modules/field/tests/src/Functional/Number/NumberFieldTest.php index 9450f5f8c3cf..93030b0f9290 100644 --- a/core/modules/field/tests/src/Functional/Number/NumberFieldTest.php +++ b/core/modules/field/tests/src/Functional/Number/NumberFieldTest.php @@ -284,6 +284,17 @@ public function testNumberIntegerField() { // Verify that the "content" attribute has been set to the value of the // field, and the prefix is being displayed. $this->assertSession()->elementTextContains('xpath', '//div[@content="' . $integer_value . '"]', 'ThePrefix' . $integer_value); + + $field_configuration_url = 'entity_test/structure/entity_test/fields/entity_test.entity_test.' . $field_name; + $this->drupalGet($field_configuration_url); + + // Tests Number validation messages. + $edit = [ + 'settings[min]' => 10, + 'settings[max]' => 8, + ]; + $this->submitForm($edit, 'Save settings'); + $this->assertSession()->pageTextContains("The minimum value must be less than or equal to {$edit['settings[max]']}."); } /** diff --git a/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php b/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php index 37030b4925c3..6d5f6b4a58a0 100644 --- a/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php +++ b/core/modules/field/tests/src/Kernel/Number/NumberItemTest.php @@ -4,6 +4,8 @@ use Drupal\Core\Field\FieldItemInterface; use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\Field\Plugin\Field\FieldType\NumericItemBase; +use Drupal\Core\Form\FormState; use Drupal\entity_test\Entity\EntityTest; use Drupal\field\Entity\FieldConfig; use Drupal\Tests\field\Kernel\FieldKernelTestBase; @@ -188,4 +190,68 @@ public function dataNumberFieldSettingsProvider() { yield ['decimal', 1, 2, 1.5, FALSE]; } + /** + * Tests the validation of minimum and maximum values. + * + * @param int|float|string $min + * Min value to be tested. + * @param int|float|string $max + * Max value to be tested. + * @param int|float|string $value + * Value to be tested with min and max values. + * @param bool $hasError + * Expected validation result. + * @param string $message + * (optional) Error message result. + * + * @dataProvider dataTestMinMaxValue + */ + public function testFormFieldMinMaxValue(int|float|string $min, int|float|string $max, int|float|string $value, bool $hasError, string $message = ''): void { + $element = [ + '#type' => 'number', + '#title' => 'min', + '#default_value' => $min, + '#element_validate' => [[NumericItemBase::class, 'validateMinAndMaxConfig']], + '#description' => 'The minimum value that should be allowed in this field. Leave blank for no minimum.', + '#parents' => [], + '#name' => 'min', + ]; + + $form_state = new FormState(); + $form_state->setValue('min', $value); + $form_state->setValue('settings', [ + 'min' => $min, + 'max' => $max, + 'prefix' => '', + 'suffix' => '', + 'precision' => 10, + 'scale' => 2, + ]); + $completed_form = []; + NumericItemBase::validateMinAndMaxConfig($element, $form_state, $completed_form); + $errors = $form_state->getErrors(); + $this->assertEquals($hasError, count($errors) > 0); + if ($errors) { + $error = current($errors); + $this->assertEquals($error, $message); + } + } + + /** + * Data provider for testFormFieldMinMaxValue(). + * + * @return \Generator + * The test data. + */ + public function dataTestMinMaxValue() { + yield [1, 10, 5, FALSE, '']; + yield [10, 5, 6, TRUE, 'The minimum value must be less than or equal to 5.']; + yield [1, 0, 6, TRUE, 'The minimum value must be less than or equal to 0.']; + yield [0, -2, 0.5, TRUE, 'The minimum value must be less than or equal to -2.']; + yield [-10, -20, -5, TRUE, 'The minimum value must be less than or equal to -20.']; + yield [1, '', -5, FALSE, '']; + yield ['', '', '', FALSE, '']; + yield ['2', '1', '', TRUE, 'The minimum value must be less than or equal to 1.']; + } + } -- GitLab