mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 15:51:48 +03:00
Add Validation Group Support to SymfonyConstraintAnnotationReader
This support is gated behind a flag as turning it on seems like it would be a large backwards incompatible change.
This commit is contained in:
parent
0ed76d7d24
commit
cc962b72c8
@ -52,6 +52,6 @@ class AnnotationsReader
|
||||
{
|
||||
$this->openApiAnnotationsReader->updateProperty($reflection, $property, $serializationGroups);
|
||||
$this->phpDocReader->updateProperty($reflection, $property);
|
||||
$this->symfonyConstraintAnnotationReader->updateProperty($reflection, $property);
|
||||
$this->symfonyConstraintAnnotationReader->updateProperty($reflection, $property, $serializationGroups);
|
||||
}
|
||||
}
|
||||
|
@ -32,9 +32,15 @@ class SymfonyConstraintAnnotationReader
|
||||
*/
|
||||
private $schema;
|
||||
|
||||
public function __construct(Reader $annotationsReader)
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $useValidationGroups;
|
||||
|
||||
public function __construct(Reader $annotationsReader, bool $useValidationGroups=false)
|
||||
{
|
||||
$this->annotationsReader = $annotationsReader;
|
||||
$this->useValidationGroups = $useValidationGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,9 +48,9 @@ class SymfonyConstraintAnnotationReader
|
||||
*
|
||||
* @param \ReflectionProperty|\ReflectionMethod $reflection
|
||||
*/
|
||||
public function updateProperty($reflection, OA\Property $property): void
|
||||
public function updateProperty($reflection, OA\Property $property, ?array $validationGroups = null): void
|
||||
{
|
||||
foreach ($this->getAnnotations($reflection) as $outerAnnotation) {
|
||||
foreach ($this->getAnnotations($reflection, $validationGroups) as $outerAnnotation) {
|
||||
$innerAnnotations = $outerAnnotation instanceof Assert\Compound
|
||||
? $outerAnnotation->constraints
|
||||
: [$outerAnnotation];
|
||||
@ -175,7 +181,23 @@ class SymfonyConstraintAnnotationReader
|
||||
/**
|
||||
* @param \ReflectionProperty|\ReflectionMethod $reflection
|
||||
*/
|
||||
private function getAnnotations($reflection): \Traversable
|
||||
private function getAnnotations($reflection, ?array $validationGroups): iterable
|
||||
{
|
||||
foreach ($this->locateAnnotations($reflection) as $annotation) {
|
||||
if (! $annotation instanceof Constraint) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->useValidationGroups || $this->isConstraintInGroup($annotation, $validationGroups)) {
|
||||
yield $annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionProperty|\ReflectionMethod $reflection
|
||||
*/
|
||||
private function locateAnnotations($reflection): \Traversable
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 80000 && class_exists(Constraint::class)) {
|
||||
foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
|
||||
@ -189,4 +211,20 @@ class SymfonyConstraintAnnotationReader
|
||||
yield from $this->annotationsReader->getMethodAnnotations($reflection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the given constraint is in the provided serialization groups.
|
||||
*
|
||||
* If no groups are provided the validator would run in the Constraint::DEFAULT_GROUP,
|
||||
* and constraints without any `groups` passed to them would be in that same
|
||||
* default group. So even with a null $validationGroups passed here there still
|
||||
* has to be a check on the default group.
|
||||
*/
|
||||
private function isConstraintInGroup(Constraint $annotation, ?array $validationGroups) : bool
|
||||
{
|
||||
return count(array_intersect(
|
||||
$validationGroups ?: [Constraint::DEFAULT_GROUP],
|
||||
$annotation->groups
|
||||
)) > 0;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\SymfonyConstraintAnnotationReader;
|
||||
use Nelmio\ApiDocBundle\Tests\Helper;
|
||||
use Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture as CustomAssert;
|
||||
@ -423,4 +424,125 @@ class SymfonyConstraintAnnotationReaderTest extends TestCase
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* re-using another provider here, since all constraints land in the default
|
||||
* group when `group={"someGroup"}` is not set.
|
||||
*
|
||||
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1857
|
||||
* @dataProvider provideRangeConstraintDoesNotSetMinimumIfMinIsNotSet
|
||||
*/
|
||||
public function testReaderWithValidationGroupsEnabledChecksForDefaultGroupWhenNoSerializationGroupsArePassed($entity)
|
||||
{
|
||||
$schema = new OA\Schema([]);
|
||||
$schema->merge([new OA\Property(['property' => 'property1'])]);
|
||||
$reader = $this->createConstraintReaderWithValidationGroupsEnabled();
|
||||
$reader->setSchema($schema);
|
||||
|
||||
// no serialization groups passed here
|
||||
$reader->updateProperty(
|
||||
new \ReflectionProperty($entity, 'property1'),
|
||||
$schema->properties[0]
|
||||
);
|
||||
|
||||
$this->assertSame(10, $schema->properties[0]->maximum, 'should have read constraints in the default group');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1857
|
||||
* @dataProvider provideConstraintsWithGroups
|
||||
*/
|
||||
public function testReaderWithValidationGroupsEnabledDoesNotReadAnnotationsWithoutDefaultGroupIfNoGroupsArePassed($entity)
|
||||
{
|
||||
$schema = new OA\Schema([]);
|
||||
$schema->merge([
|
||||
new OA\Property(['property' => 'property1']),
|
||||
]);
|
||||
$reader = $this->createConstraintReaderWithValidationGroupsEnabled();
|
||||
$reader->setSchema($schema);
|
||||
|
||||
// no serialization groups passed here
|
||||
$reader->updateProperty(
|
||||
new \ReflectionProperty($entity, 'property1'),
|
||||
$schema->properties[0]
|
||||
);
|
||||
|
||||
$this->assertSame(['property1'], $schema->required, 'should have read constraint in default group');
|
||||
$this->assertSame(OA\UNDEFINED, $schema->properties[0]->minimum, 'should not have read constraint in other group');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1857
|
||||
* @dataProvider provideConstraintsWithGroups
|
||||
*/
|
||||
public function testReaderWithValidationGroupsEnabledReadsOnlyConstraintsWithGroupsProvided($entity)
|
||||
{
|
||||
$schema = new OA\Schema([]);
|
||||
$schema->merge([
|
||||
new OA\Property(['property' => 'property1']),
|
||||
]);
|
||||
$reader = $this->createConstraintReaderWithValidationGroupsEnabled();
|
||||
$reader->setSchema($schema);
|
||||
|
||||
// no serialization groups passed here
|
||||
$reader->updateProperty(
|
||||
new \ReflectionProperty($entity, 'property1'),
|
||||
$schema->properties[0],
|
||||
["other"],
|
||||
);
|
||||
|
||||
$this->assertSame(OA\UNDEFINED, $schema->required, 'should not have read constraint in default group');
|
||||
$this->assertSame(1, $schema->properties[0]->minimum, 'should have read constraint in other group');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1857
|
||||
* @dataProvider provideConstraintsWithGroups
|
||||
*/
|
||||
public function testReaderWithValidationGroupsEnabledCanReadFromMultipleValidationGroups($entity)
|
||||
{
|
||||
$schema = new OA\Schema([]);
|
||||
$schema->merge([
|
||||
new OA\Property(['property' => 'property1']),
|
||||
]);
|
||||
$reader = $this->createConstraintReaderWithValidationGroupsEnabled();
|
||||
$reader->setSchema($schema);
|
||||
|
||||
// no serialization groups passed here
|
||||
$reader->updateProperty(
|
||||
new \ReflectionProperty($entity, 'property1'),
|
||||
$schema->properties[0],
|
||||
["other", Constraint::DEFAULT_GROUP],
|
||||
);
|
||||
|
||||
$this->assertSame(['property1'], $schema->required, 'should have read constraint in default group');
|
||||
$this->assertSame(1, $schema->properties[0]->minimum, 'should have read constraint in other group');
|
||||
}
|
||||
|
||||
public function provideConstraintsWithGroups(): iterable
|
||||
{
|
||||
yield 'Annotations' => [new class() {
|
||||
/**
|
||||
* @Assert\NotBlank()
|
||||
* @Assert\Range(min=1, groups={"other"})
|
||||
*/
|
||||
private $property1;
|
||||
}];
|
||||
|
||||
if (\PHP_VERSION_ID >= 80000) {
|
||||
yield 'Attributes' => [new class() {
|
||||
#[Assert\NotBlank()]
|
||||
#[Assert\Range(min: 1, group: ["other"])]
|
||||
private $property1;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
private function createConstraintReaderWithValidationGroupsEnabled() : SymfonyConstraintAnnotationReader
|
||||
{
|
||||
return new SymfonyConstraintAnnotationReader(
|
||||
new AnnotationReader(),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user