diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 183267cf26287..addca159de6a3 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 6.4 --- + * Add `is_valid` function to the `Expression` constraint, its behavior is the same as `ValidatorInterface::validate` * Allow single integer for the `versions` option of the `Uuid` constraint * Allow single constraint to be passed to the `constraints` option of the `When` constraint * Deprecate Doctrine annotations support in favor of native attributes diff --git a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php index d1fe60a791084..45026f878471c 100644 --- a/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php +++ b/src/Symfony/Component/Validator/Constraints/ExpressionValidator.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Validator\Constraints; +use Symfony\Component\ExpressionLanguage\ExpressionFunction; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -20,13 +22,16 @@ * @author Fabien Potencier * @author Bernhard Schussek */ -class ExpressionValidator extends ConstraintValidator +class ExpressionValidator extends ConstraintValidator implements ExpressionFunctionProviderInterface { - private ?ExpressionLanguage $expressionLanguage; + private ExpressionLanguage $expressionLanguage; public function __construct(ExpressionLanguage $expressionLanguage = null) { - $this->expressionLanguage = $expressionLanguage; + if ($expressionLanguage) { + $this->expressionLanguage = clone $expressionLanguage; + $this->expressionLanguage->registerProvider($this); + } } /** @@ -41,6 +46,7 @@ public function validate(mixed $value, Constraint $constraint) $variables = $constraint->values; $variables['value'] = $value; $variables['this'] = $this->context->getObject(); + $variables['context'] = $this->context; if ($constraint->negate xor $this->getExpressionLanguage()->evaluate($constraint->expression, $variables)) { $this->context->buildViolation($constraint->message) @@ -50,8 +56,27 @@ public function validate(mixed $value, Constraint $constraint) } } + public function getFunctions(): array + { + return [ + new ExpressionFunction('is_valid', function (...$arguments) { + return sprintf( + '0 === $context->getValidator()->inContext($context)->validate(%s)->getViolations()->count()', + implode(', ', $arguments) + ); + }, function (array $variables, ...$arguments): bool { + return 0 === $variables['context']->getValidator()->inContext($variables['context'])->validate(...$arguments)->getViolations()->count(); + }), + ]; + } + private function getExpressionLanguage(): ExpressionLanguage { - return $this->expressionLanguage ??= new ExpressionLanguage(); + if (!isset($this->expressionLanguage)) { + $this->expressionLanguage = new ExpressionLanguage(); + $this->expressionLanguage->registerProvider($this); + } + + return $this->expressionLanguage; } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php index f427604725a2f..132507c923af7 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionValidatorTest.php @@ -14,6 +14,9 @@ use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Validator\Constraints\Expression; use Symfony\Component\Validator\Constraints\ExpressionValidator; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\Range; +use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; use Symfony\Component\Validator\Tests\Fixtures\NestedAttribute\Entity; use Symfony\Component\Validator\Tests\Fixtures\ToString; @@ -304,4 +307,81 @@ public function testViolationOnPass() ->setCode(Expression::EXPRESSION_FAILED_ERROR) ->assertRaised(); } + + public function testIsValidExpression() + { + $constraints = [new NotNull(), new Range(['min' => 2])]; + + $constraint = new Expression( + ['expression' => 'is_valid(this.data, a)', 'values' => ['a' => $constraints]] + ); + + $object = new Entity(); + $object->data = 7; + + $this->setObject($object); + + $this->expectValidateValue(0, $object->data, $constraints); + + $this->validator->validate($object, $constraint); + + $this->assertNoViolation(); + } + + public function testIsValidExpressionInvalid() + { + $constraints = [new Range(['min' => 2, 'max' => 5])]; + + $constraint = new Expression( + ['expression' => 'is_valid(this.data, a)', 'values' => ['a' => $constraints]] + ); + + $object = new Entity(); + $object->data = 7; + + $this->setObject($object); + + $this->expectFailingValueValidation( + 0, + 7, + $constraints, + null, + new ConstraintViolation('error_range', '', [], '', '', 7, null, 'range') + ); + + $this->validator->validate($object, $constraint); + + $this->assertCount(2, $this->context->getViolations()); + } + + /** + * @dataProvider provideCompileIsValid + */ + public function testCompileIsValid(string $expression, array $names, string $expected) + { + $provider = new ExpressionValidator(); + + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->registerProvider($provider); + + $result = $expressionLanguage->compile($expression, $names); + + $this->assertSame($expected, $result); + } + + public static function provideCompileIsValid(): array + { + return [ + [ + 'is_valid("foo", constraints)', + ['constraints'], + '0 === $context->getValidator()->inContext($context)->validate("foo", $constraints)->getViolations()->count()', + ], + [ + 'is_valid(this.data, constraints, groups)', + ['this', 'constraints', 'groups'], + '0 === $context->getValidator()->inContext($context)->validate($this->data, $constraints, $groups)->getViolations()->count()', + ], + ]; + } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy