From de3c54bf6ee15986d4182c9a577992a96ce4ef35 Mon Sep 17 00:00:00 2001 From: Courtney Miles Date: Mon, 28 Jul 2025 14:23:37 +1000 Subject: [PATCH] feat: Update cast exception to include original value Prior, the cast exception would contain the cast-value and not the original value, which can be confusing when reporting the validation error to users. --- src/Fields/BaseField.php | 30 +++++++++++++----------- tests/Fields/BaseFieldTest.php | 43 +++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/Fields/BaseField.php b/src/Fields/BaseField.php index ffcc22e..d86e975 100644 --- a/src/Fields/BaseField.php +++ b/src/Fields/BaseField.php @@ -151,17 +151,25 @@ final public function castValue($val) } return null; - } else { - $val = $this->validateCastValue($val); - if (!$this->constraintsDisabled) { - $validationErrors = $this->checkConstraints($val); - if (count($validationErrors) > 0) { - throw new FieldValidationException($validationErrors); + } + + $castVal = $this->validateCastValue($val); + + if (!$this->constraintsDisabled) { + $validationErrors = $this->checkConstraints($castVal); + + if (count($validationErrors) > 0) { + foreach ($validationErrors as $i => $schemaError) { + \assert($schemaError instanceof SchemaValidationError); + $schemaError->extraDetails['value'] = $val; + $validationErrors[$i] = $schemaError; } - } - return $val; + throw new FieldValidationException($validationErrors); + } } + + return $castVal; } public function validateValue($val) @@ -171,12 +179,6 @@ public function validateValue($val) return []; } catch (FieldValidationException $e) { - foreach ($e->validationErrors as $ve) { - // Replace the cast-value for the violation, with the original value. - // This so the error message contains the original representation of the invalid value. - $ve->extraDetails['value'] = $val; - } - return $e->validationErrors; } } diff --git a/tests/Fields/BaseFieldTest.php b/tests/Fields/BaseFieldTest.php index 6c594f3..eb95894 100644 --- a/tests/Fields/BaseFieldTest.php +++ b/tests/Fields/BaseFieldTest.php @@ -4,6 +4,7 @@ namespace Fields; +use frictionlessdata\tableschema\Exceptions\FieldValidationException; use frictionlessdata\tableschema\Fields\BaseField; use PHPUnit\Framework\TestCase; @@ -12,7 +13,7 @@ */ class BaseFieldTest extends TestCase { - public function testPreserveOriginalValueInValidationError(): void + public function testPreserveOriginalValueInValidateError(): void { $descriptor = (object) [ 'name' => 'date_col', @@ -38,4 +39,44 @@ protected function validateCastValue($val) $error->extraDetails['value'] ); } + + public function testPreserveOriginalValueInCastError(): void + { + $descriptor = (object) [ + 'name' => 'date_col', + 'constraints' => (object) ['minimum' => '2025-07-01'], + ]; + + $sut = new class($descriptor) extends BaseField { + protected function validateCastValue($val) + { + // If the logic is wrong, this object will be in the error + // instead of the original date string. + return new \DateTimeImmutable($val); + } + }; + + $validatedValue = '2025-06-30'; + $exception = null; + + try { + $sut->castValue($validatedValue); + } catch (FieldValidationException $exception) { + } + + self::assertNotNull($exception, 'An exception is expected for this test.'); + self::assertSame( + 'date_col: value is below minimum ("2025-06-30")', + $exception->getMessage() + ); + + $errors = $exception->validationErrors; + + self::assertCount(1, $errors); + $error = reset($errors); + self::assertSame( + $validatedValue, + $error->extraDetails['value'] + ); + } }