diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f0f35f742d..129a65473ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 2.7.0 +* JSON Schema: Manage Compound constraint when generating property metadata (#4180) * Validator: Add an option to disable query parameter validation (#4165) * JSON Schema: Add support for generating property schema with Choice restriction (#4162) * JSON Schema: Add support for generating property schema with Range restriction (#4158) diff --git a/src/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php b/src/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php index 1f1706597a6..9e8babef41d 100644 --- a/src/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php +++ b/src/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php @@ -19,6 +19,7 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Bic; use Symfony\Component\Validator\Constraints\CardScheme; +use Symfony\Component\Validator\Constraints\Compound; use Symfony\Component\Validator\Constraints\Currency; use Symfony\Component\Validator\Constraints\Date; use Symfony\Component\Validator\Constraints\DateTime; @@ -171,7 +172,7 @@ private function getPropertyConstraints( } foreach ($validatorPropertyMetadata->findConstraints($validationGroup) as $propertyConstraint) { - if ($propertyConstraint instanceof Sequentially) { + if ($propertyConstraint instanceof Sequentially || $propertyConstraint instanceof Compound) { $constraints[] = $propertyConstraint->getNestedContraints(); } else { $constraints[] = [$propertyConstraint]; diff --git a/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php b/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php index b7cfa83f6a2..389789c426c 100644 --- a/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php +++ b/tests/Bridge/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php @@ -24,6 +24,7 @@ use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface; use ApiPlatform\Core\Metadata\Property\PropertyMetadata; use ApiPlatform\Core\Tests\Fixtures\DummyAtLeastOneOfValidatedEntity; +use ApiPlatform\Core\Tests\Fixtures\DummyCompoundValidatedEntity; use ApiPlatform\Core\Tests\Fixtures\DummyIriWithValidationEntity; use ApiPlatform\Core\Tests\Fixtures\DummyRangeValidatedEntity; use ApiPlatform\Core\Tests\Fixtures\DummySequentiallyValidatedEntity; @@ -35,6 +36,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Validator\Constraints\AtLeastOneOf; +use Symfony\Component\Validator\Constraints\Compound; use Symfony\Component\Validator\Constraints\Sequentially; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; @@ -378,6 +380,37 @@ public function testCreateWithSequentiallyConstraint(): void $this->assertArrayHasKey('pattern', $schema); } + public function testCreateWithCompoundConstraint(): void + { + if (!class_exists(Compound::class)) { + $this->markTestSkipped(); + } + + $validatorClassMetadata = new ClassMetadata(DummyCompoundValidatedEntity::class); + (new AnnotationLoader(new AnnotationReader()))->loadClassMetadata($validatorClassMetadata); + + $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class); + $validatorMetadataFactory->getMetadataFor(DummyCompoundValidatedEntity::class) + ->willReturn($validatorClassMetadata) + ->shouldBeCalled(); + + $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class); + $decoratedPropertyMetadataFactory->create(DummyCompoundValidatedEntity::class, 'dummy', [])->willReturn( + new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING)) + )->shouldBeCalled(); + $validationPropertyMetadataFactory = new ValidatorPropertyMetadataFactory( + $validatorMetadataFactory->reveal(), + $decoratedPropertyMetadataFactory->reveal(), + [new PropertySchemaLengthRestriction(), new PropertySchemaRegexRestriction()] + ); + $schema = $validationPropertyMetadataFactory->create(DummyCompoundValidatedEntity::class, 'dummy')->getSchema(); + + $this->assertNotNull($schema); + $this->assertArrayHasKey('minLength', $schema); + $this->assertArrayHasKey('maxLength', $schema); + $this->assertArrayHasKey('pattern', $schema); + } + public function testCreateWithAtLeastOneOfConstraint(): void { if (!class_exists(AtLeastOneOf::class)) { diff --git a/tests/Fixtures/DummyCompoundValidatedEntity.php b/tests/Fixtures/DummyCompoundValidatedEntity.php new file mode 100644 index 00000000000..40e72cf5155 --- /dev/null +++ b/tests/Fixtures/DummyCompoundValidatedEntity.php @@ -0,0 +1,26 @@ + + * + * 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 ApiPlatform\Core\Tests\Fixtures\TestBundle\Validator\Constraint\DummyCompoundRequirements; + +class DummyCompoundValidatedEntity +{ + /** + * @var string + * + * @DummyCompoundRequirements + */ + public $dummy; +} diff --git a/tests/Fixtures/TestBundle/Validator/Constraint/DummyCompoundRequirements.php b/tests/Fixtures/TestBundle/Validator/Constraint/DummyCompoundRequirements.php new file mode 100644 index 00000000000..cb70c8f4d1d --- /dev/null +++ b/tests/Fixtures/TestBundle/Validator/Constraint/DummyCompoundRequirements.php @@ -0,0 +1,32 @@ + + * + * 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\TestBundle\Validator\Constraint; + +use Symfony\Component\Validator\Constraints\Compound; +use Symfony\Component\Validator\Constraints\Length; +use Symfony\Component\Validator\Constraints\Regex; + +/** + * @Annotation + */ +final class DummyCompoundRequirements extends Compound +{ + public function getConstraints(array $options): array + { + return [ + new Length(['min' => 1, 'max' => 32]), + new Regex(['pattern' => '/^[a-z]$/']), + ]; + } +}