diff --git a/CHANGELOG.md b/CHANGELOG.md
index ea73b393bdc..c6be371153e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
## 2.7.0
+* JSON Schema: Add support for generating property schema with numeric constraint restrictions (#4225)
* JSON Schema: Add support for generating property schema with Collection restriction (#4182)
* JSON Schema: Add support for generating property schema format for Url and Hostname (#4185)
* JSON Schema: Add support for generating property schema with Count restriction (#4186)
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/validator.xml b/src/Bridge/Symfony/Bundle/Resources/config/validator.xml
index d05525f5e6c..8e0a2c3f22b 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/validator.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/validator.xml
@@ -30,10 +30,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanOrEqualRestriction.php b/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanOrEqualRestriction.php
new file mode 100644
index 00000000000..4380b8c140b
--- /dev/null
+++ b/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanOrEqualRestriction.php
@@ -0,0 +1,45 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction;
+
+use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
+use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
+
+/**
+ * @author Tomas Norkūnas
+ */
+final class PropertySchemaGreaterThanOrEqualRestriction implements PropertySchemaRestrictionMetadataInterface
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @param GreaterThanOrEqual $constraint
+ */
+ public function create(Constraint $constraint, PropertyMetadata $propertyMetadata): array
+ {
+ return [
+ 'minimum' => $constraint->value,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supports(Constraint $constraint, PropertyMetadata $propertyMetadata): bool
+ {
+ return $constraint instanceof GreaterThanOrEqual && is_numeric($constraint->value) && null !== ($type = $propertyMetadata->getType()) && \in_array($type->getBuiltinType(), [Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_FLOAT], true);
+ }
+}
diff --git a/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanRestriction.php b/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanRestriction.php
new file mode 100644
index 00000000000..044df1cf7ce
--- /dev/null
+++ b/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanRestriction.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction;
+
+use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
+use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\GreaterThan;
+
+/**
+ * @author Tomas Norkūnas
+ */
+final class PropertySchemaGreaterThanRestriction implements PropertySchemaRestrictionMetadataInterface
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @param GreaterThan $constraint
+ */
+ public function create(Constraint $constraint, PropertyMetadata $propertyMetadata): array
+ {
+ return [
+ 'minimum' => $constraint->value,
+ 'exclusiveMinimum' => true,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supports(Constraint $constraint, PropertyMetadata $propertyMetadata): bool
+ {
+ return $constraint instanceof GreaterThan && is_numeric($constraint->value) && null !== ($type = $propertyMetadata->getType()) && \in_array($type->getBuiltinType(), [Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_FLOAT], true);
+ }
+}
diff --git a/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanOrEqualRestriction.php b/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanOrEqualRestriction.php
new file mode 100644
index 00000000000..592eb8b479e
--- /dev/null
+++ b/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanOrEqualRestriction.php
@@ -0,0 +1,45 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction;
+
+use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
+use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\LessThanOrEqual;
+
+/**
+ * @author Tomas Norkūnas
+ */
+final class PropertySchemaLessThanOrEqualRestriction implements PropertySchemaRestrictionMetadataInterface
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @param LessThanOrEqual $constraint
+ */
+ public function create(Constraint $constraint, PropertyMetadata $propertyMetadata): array
+ {
+ return [
+ 'maximum' => $constraint->value,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supports(Constraint $constraint, PropertyMetadata $propertyMetadata): bool
+ {
+ return $constraint instanceof LessThanOrEqual && is_numeric($constraint->value) && null !== ($type = $propertyMetadata->getType()) && \in_array($type->getBuiltinType(), [Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_FLOAT], true);
+ }
+}
diff --git a/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanRestriction.php b/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanRestriction.php
new file mode 100644
index 00000000000..cf5c8cca153
--- /dev/null
+++ b/src/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanRestriction.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction;
+
+use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
+use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\LessThan;
+
+/**
+ * @author Tomas Norkūnas
+ */
+final class PropertySchemaLessThanRestriction implements PropertySchemaRestrictionMetadataInterface
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @param LessThan $constraint
+ */
+ public function create(Constraint $constraint, PropertyMetadata $propertyMetadata): array
+ {
+ return [
+ 'maximum' => $constraint->value,
+ 'exclusiveMaximum' => true,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supports(Constraint $constraint, PropertyMetadata $propertyMetadata): bool
+ {
+ return $constraint instanceof LessThan && is_numeric($constraint->value) && null !== ($type = $propertyMetadata->getType()) && \in_array($type->getBuiltinType(), [Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_FLOAT], true);
+ }
+}
diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php
index 1f5b861ee64..2d9e3f85ec2 100644
--- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php
+++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php
@@ -1360,7 +1360,11 @@ private function getBaseContainerBuilderProphecyWithoutDefaultMetadataLoading(ar
'api_platform.metadata.property_schema.choice_restriction',
'api_platform.metadata.property_schema.collection_restriction',
'api_platform.metadata.property_schema.count_restriction',
+ 'api_platform.metadata.property_schema.greater_than_or_equal_restriction',
+ 'api_platform.metadata.property_schema.greater_than_restriction',
'api_platform.metadata.property_schema.length_restriction',
+ 'api_platform.metadata.property_schema.less_than_or_equal_restriction',
+ 'api_platform.metadata.property_schema.less_than_restriction',
'api_platform.metadata.property_schema.one_of_restriction',
'api_platform.metadata.property_schema.range_restriction',
'api_platform.metadata.property_schema.regex_restriction',
diff --git a/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanOrEqualRestrictionTest.php b/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanOrEqualRestrictionTest.php
new file mode 100644
index 00000000000..517155ffd4b
--- /dev/null
+++ b/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanOrEqualRestrictionTest.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Tests\Bridge\Symfony\Validator\Metadata\Property\Restriction;
+
+use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaGreaterThanOrEqualRestriction;
+use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
+use ApiPlatform\Core\Tests\ProphecyTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
+use Symfony\Component\Validator\Constraints\Positive;
+use Symfony\Component\Validator\Constraints\PositiveOrZero;
+
+/**
+ * @author Tomas Norkūnas
+ */
+final class PropertySchemaGreaterThanOrEqualRestrictionTest extends TestCase
+{
+ use ProphecyTrait;
+
+ private $propertySchemaGreaterThanOrEqualRestriction;
+
+ protected function setUp(): void
+ {
+ $this->propertySchemaGreaterThanOrEqualRestriction = new PropertySchemaGreaterThanOrEqualRestriction();
+ }
+
+ /**
+ * @dataProvider supportsProvider
+ */
+ public function testSupports(Constraint $constraint, PropertyMetadata $propertyMetadata, bool $expectedResult): void
+ {
+ self::assertSame($expectedResult, $this->propertySchemaGreaterThanOrEqualRestriction->supports($constraint, $propertyMetadata));
+ }
+
+ public function supportsProvider(): \Generator
+ {
+ yield 'supported int' => [new GreaterThanOrEqual(['value' => 10]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), true];
+ yield 'supported float' => [new GreaterThanOrEqual(['value' => 10.99]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT)), true];
+ yield 'supported positive or zero' => [new PositiveOrZero(), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), true];
+ yield 'not supported positive' => [new Positive(), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), false];
+ yield 'not supported property path' => [new GreaterThanOrEqual(['propertyPath' => 'greaterThanMe']), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), false];
+ }
+
+ public function testCreate(): void
+ {
+ self::assertSame(['minimum' => 10], $this->propertySchemaGreaterThanOrEqualRestriction->create(new GreaterThanOrEqual(['value' => 10]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT))));
+ }
+}
diff --git a/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanRestrictionTest.php b/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanRestrictionTest.php
new file mode 100644
index 00000000000..e19669ef2aa
--- /dev/null
+++ b/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaGreaterThanRestrictionTest.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Tests\Bridge\Symfony\Validator\Metadata\Property\Restriction;
+
+use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaGreaterThanRestriction;
+use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
+use ApiPlatform\Core\Tests\ProphecyTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\GreaterThan;
+use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
+use Symfony\Component\Validator\Constraints\Positive;
+use Symfony\Component\Validator\Constraints\PositiveOrZero;
+
+/**
+ * @author Tomas Norkūnas
+ */
+final class PropertySchemaGreaterThanRestrictionTest extends TestCase
+{
+ use ProphecyTrait;
+
+ private $propertySchemaGreaterThanRestriction;
+
+ protected function setUp(): void
+ {
+ $this->propertySchemaGreaterThanRestriction = new PropertySchemaGreaterThanRestriction();
+ }
+
+ /**
+ * @dataProvider supportsProvider
+ */
+ public function testSupports(Constraint $constraint, PropertyMetadata $propertyMetadata, bool $expectedResult): void
+ {
+ self::assertSame($expectedResult, $this->propertySchemaGreaterThanRestriction->supports($constraint, $propertyMetadata));
+ }
+
+ public function supportsProvider(): \Generator
+ {
+ yield 'supported int' => [new GreaterThan(['value' => 10]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), true];
+ yield 'supported float' => [new GreaterThan(['value' => 10.99]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT)), true];
+ yield 'supported positive' => [new Positive(), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), true];
+ yield 'not supported positive or zero' => [new PositiveOrZero(), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), false];
+ yield 'not supported property path' => [new GreaterThan(['propertyPath' => 'greaterThanMe']), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), false];
+ }
+
+ public function testCreate(): void
+ {
+ self::assertSame([
+ 'minimum' => 10,
+ 'exclusiveMinimum' => true,
+ ], $this->propertySchemaGreaterThanRestriction->create(new GreaterThanOrEqual(['value' => 10]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT))));
+ }
+}
diff --git a/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanOrEqualRestrictionTest.php b/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanOrEqualRestrictionTest.php
new file mode 100644
index 00000000000..ea97bb04587
--- /dev/null
+++ b/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanOrEqualRestrictionTest.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Tests\Bridge\Symfony\Validator\Metadata\Property\Restriction;
+
+use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaLessThanOrEqualRestriction;
+use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
+use ApiPlatform\Core\Tests\ProphecyTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\LessThanOrEqual;
+use Symfony\Component\Validator\Constraints\Negative;
+use Symfony\Component\Validator\Constraints\NegativeOrZero;
+
+/**
+ * @author Tomas Norkūnas
+ */
+final class PropertySchemaLessThanOrEqualRestrictionTest extends TestCase
+{
+ use ProphecyTrait;
+
+ private $propertySchemaLessThanOrEqualRestriction;
+
+ protected function setUp(): void
+ {
+ $this->propertySchemaLessThanOrEqualRestriction = new PropertySchemaLessThanOrEqualRestriction();
+ }
+
+ /**
+ * @dataProvider supportsProvider
+ */
+ public function testSupports(Constraint $constraint, PropertyMetadata $propertyMetadata, bool $expectedResult): void
+ {
+ self::assertSame($expectedResult, $this->propertySchemaLessThanOrEqualRestriction->supports($constraint, $propertyMetadata));
+ }
+
+ public function supportsProvider(): \Generator
+ {
+ yield 'supported int' => [new LessThanOrEqual(['value' => 10]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), true];
+ yield 'supported float' => [new LessThanOrEqual(['value' => 10.99]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT)), true];
+ yield 'supported negative or zero' => [new NegativeOrZero(), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), true];
+ yield 'not supported negative' => [new Negative(), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), false];
+ yield 'not supported property path' => [new LessThanOrEqual(['propertyPath' => 'greaterThanMe']), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), false];
+ }
+
+ public function testCreate(): void
+ {
+ self::assertSame(['maximum' => 10], $this->propertySchemaLessThanOrEqualRestriction->create(new LessThanOrEqual(['value' => 10]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT))));
+ }
+}
diff --git a/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanRestrictionTest.php b/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanRestrictionTest.php
new file mode 100644
index 00000000000..c65d855e3f9
--- /dev/null
+++ b/tests/Bridge/Symfony/Validator/Metadata/Property/Restriction/PropertySchemaLessThanRestrictionTest.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Tests\Bridge\Symfony\Validator\Metadata\Property\Restriction;
+
+use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaLessThanRestriction;
+use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
+use ApiPlatform\Core\Tests\ProphecyTrait;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\LessThan;
+use Symfony\Component\Validator\Constraints\Negative;
+use Symfony\Component\Validator\Constraints\NegativeOrZero;
+
+/**
+ * @author Tomas Norkūnas
+ */
+final class PropertySchemaLessThanRestrictionTest extends TestCase
+{
+ use ProphecyTrait;
+
+ private $propertySchemaLessThanRestriction;
+
+ protected function setUp(): void
+ {
+ $this->propertySchemaLessThanRestriction = new PropertySchemaLessThanRestriction();
+ }
+
+ /**
+ * @dataProvider supportsProvider
+ */
+ public function testSupports(Constraint $constraint, PropertyMetadata $propertyMetadata, bool $expectedResult): void
+ {
+ self::assertSame($expectedResult, $this->propertySchemaLessThanRestriction->supports($constraint, $propertyMetadata));
+ }
+
+ public function supportsProvider(): \Generator
+ {
+ yield 'supported int' => [new LessThan(['value' => 10]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), true];
+ yield 'supported float' => [new LessThan(['value' => 10.99]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT)), true];
+ yield 'supported negative' => [new Negative(), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), true];
+ yield 'not supported negative or zero' => [new NegativeOrZero(), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), false];
+ yield 'not supported property path' => [new LessThan(['propertyPath' => 'greaterThanMe']), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)), false];
+ }
+
+ public function testCreate(): void
+ {
+ self::assertSame([
+ 'maximum' => 10,
+ 'exclusiveMaximum' => true,
+ ], $this->propertySchemaLessThanRestriction->create(new LessThan(['value' => 10]), new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT))));
+ }
+}
diff --git a/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php b/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php
index 769b8b523ab..95622b7d699 100644
--- a/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php
+++ b/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php
@@ -17,7 +17,11 @@
use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaCollectionRestriction;
use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaCountRestriction;
use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaFormat;
+use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaGreaterThanOrEqualRestriction;
+use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaGreaterThanRestriction;
use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaLengthRestriction;
+use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaLessThanOrEqualRestriction;
+use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaLessThanRestriction;
use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaOneOfRestriction;
use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRangeRestriction;
use ApiPlatform\Core\Bridge\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRegexRestriction;
@@ -30,6 +34,7 @@
use ApiPlatform\Core\Tests\Fixtures\DummyCompoundValidatedEntity;
use ApiPlatform\Core\Tests\Fixtures\DummyCountValidatedEntity;
use ApiPlatform\Core\Tests\Fixtures\DummyIriWithValidationEntity;
+use ApiPlatform\Core\Tests\Fixtures\DummyNumericValidatedEntity;
use ApiPlatform\Core\Tests\Fixtures\DummyRangeValidatedEntity;
use ApiPlatform\Core\Tests\Fixtures\DummySequentiallyValidatedEntity;
use ApiPlatform\Core\Tests\Fixtures\DummyUniqueValidatedEntity;
@@ -646,4 +651,88 @@ public function testCreateWithPropertyCollectionRestriction(): void
'required' => ['name', 'email', 'social'],
], $schema);
}
+
+ /**
+ * @dataProvider provideNumericConstraintCases
+ */
+ public function testCreateWithPropertyNumericRestriction(PropertyMetadata $propertyMetadata, string $property, array $expectedSchema): void
+ {
+ $validatorClassMetadata = new ClassMetadata(DummyNumericValidatedEntity::class);
+ (new AnnotationLoader(new AnnotationReader()))->loadClassMetadata($validatorClassMetadata);
+
+ $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class);
+ $validatorMetadataFactory->getMetadataFor(DummyNumericValidatedEntity::class)
+ ->willReturn($validatorClassMetadata)
+ ->shouldBeCalled();
+
+ $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
+ $decoratedPropertyMetadataFactory->create(DummyNumericValidatedEntity::class, $property, [])->willReturn(
+ $propertyMetadata
+ )->shouldBeCalled();
+
+ $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory(
+ $validatorMetadataFactory->reveal(), $decoratedPropertyMetadataFactory->reveal(),
+ [
+ new PropertySchemaGreaterThanOrEqualRestriction(),
+ new PropertySchemaGreaterThanRestriction(),
+ new PropertySchemaLessThanOrEqualRestriction(),
+ new PropertySchemaLessThanRestriction(),
+ ]
+ );
+
+ $schema = $validationPropertyMetadataFactory->create(DummyNumericValidatedEntity::class, $property)->getSchema();
+
+ $this->assertSame($expectedSchema, $schema);
+ }
+
+ public function provideNumericConstraintCases(): \Generator
+ {
+ yield [
+ 'propertyMetadata' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)),
+ 'property' => 'greaterThanMe',
+ 'expectedSchema' => ['minimum' => 10, 'exclusiveMinimum' => true],
+ ];
+
+ yield [
+ 'propertyMetadata' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT)),
+ 'property' => 'greaterThanOrEqualToMe',
+ 'expectedSchema' => ['minimum' => 10.99],
+ ];
+
+ yield [
+ 'propertyMetadata' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)),
+ 'property' => 'lessThanMe',
+ 'expectedSchema' => ['maximum' => 99, 'exclusiveMaximum' => true],
+ ];
+
+ yield [
+ 'propertyMetadata' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_FLOAT)),
+ 'property' => 'lessThanOrEqualToMe',
+ 'expectedSchema' => ['maximum' => 99.33],
+ ];
+
+ yield [
+ 'propertyMetadata' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)),
+ 'property' => 'positive',
+ 'expectedSchema' => ['minimum' => 0, 'exclusiveMinimum' => true],
+ ];
+
+ yield [
+ 'propertyMetadata' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)),
+ 'property' => 'positiveOrZero',
+ 'expectedSchema' => ['minimum' => 0],
+ ];
+
+ yield [
+ 'propertyMetadata' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)),
+ 'property' => 'negative',
+ 'expectedSchema' => ['maximum' => 0, 'exclusiveMaximum' => true],
+ ];
+
+ yield [
+ 'propertyMetadata' => new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT)),
+ 'property' => 'negativeOrZero',
+ 'expectedSchema' => ['maximum' => 0],
+ ];
+ }
}
diff --git a/tests/Fixtures/DummyNumericValidatedEntity.php b/tests/Fixtures/DummyNumericValidatedEntity.php
new file mode 100644
index 00000000000..83e09ff4dbc
--- /dev/null
+++ b/tests/Fixtures/DummyNumericValidatedEntity.php
@@ -0,0 +1,75 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Core\Tests\Fixtures;
+
+use Symfony\Component\Validator\Constraints as Assert;
+
+class DummyNumericValidatedEntity
+{
+ /**
+ * @var int
+ *
+ * @Assert\GreaterThan(value=10)
+ */
+ public $greaterThanMe;
+
+ /**
+ * @var float
+ *
+ * @Assert\GreaterThanOrEqual(value=10.99)
+ */
+ public $greaterThanOrEqualToMe;
+
+ /**
+ * @var int
+ *
+ * @Assert\LessThan(value=99)
+ */
+ public $lessThanMe;
+
+ /**
+ * @var float
+ *
+ * @Assert\LessThanOrEqual(value=99.33)
+ */
+ public $lessThanOrEqualToMe;
+
+ /**
+ * @var int
+ *
+ * @Assert\Positive
+ */
+ public $positive;
+
+ /**
+ * @var int
+ *
+ * @Assert\PositiveOrZero
+ */
+ public $positiveOrZero;
+
+ /**
+ * @var int
+ *
+ * @Assert\Negative
+ */
+ public $negative;
+
+ /**
+ * @var int
+ *
+ * @Assert\NegativeOrZero
+ */
+ public $negativeOrZero;
+}