diff --git a/src/Metadata/Property/Factory/ExtractorPropertyMetadataFactory.php b/src/Metadata/Property/Factory/ExtractorPropertyMetadataFactory.php index 30dc24e2e7..bbe2882848 100644 --- a/src/Metadata/Property/Factory/ExtractorPropertyMetadataFactory.php +++ b/src/Metadata/Property/Factory/ExtractorPropertyMetadataFactory.php @@ -60,17 +60,7 @@ public function create(string $resourceClass, string $property, array $options = foreach ($propertyMetadata as $key => $value) { if ('nativeType' === $key && null !== $value) { - if (class_exists(PhpDocParser::class)) { - $apiProperty = $apiProperty->withNativeType((new StringTypeResolver())->resolve($value)); - - continue; - } - - try { - $apiProperty = $apiProperty->withNativeType(Type::builtin($value)); - } catch (\ValueError) { - throw new RuntimeException(\sprintf('Cannot create a type from "%s". Try running "composer require phpstan/phpdoc-parser" to support all types.', $value)); - } + $apiProperty = $apiProperty->withNativeType($this->createNativeType($value)); continue; } @@ -106,6 +96,10 @@ private function update(ApiProperty $propertyMetadata, array $metadata): ApiProp { foreach (get_class_methods(ApiProperty::class) as $method) { if (preg_match('/^(?:get|is)(.*)/', (string) $method, $matches) && null !== ($val = $metadata[lcfirst($matches[1])] ?? null) && method_exists($propertyMetadata, "with{$matches[1]}")) { + if ('NativeType' === $matches[1]) { + $val = $this->createNativeType($val); + } + $propertyMetadata = $propertyMetadata->{"with{$matches[1]}"}($val); } } @@ -113,6 +107,19 @@ private function update(ApiProperty $propertyMetadata, array $metadata): ApiProp return $propertyMetadata; } + private function createNativeType(string $type): Type + { + if (class_exists(PhpDocParser::class)) { + return (new StringTypeResolver())->resolve($type); + } + + try { + return Type::builtin($type); + } catch (\ValueError) { + throw new RuntimeException(\sprintf('Cannot create a type from "%s". Try running "composer require phpstan/phpdoc-parser" to support all types.', $type)); + } + } + private function handleUserDefinedSchema(ApiProperty $propertyMetadata): ApiProperty { // can't know later if the schema has been defined by the user or by API Platform diff --git a/src/Metadata/Tests/Property/Factory/ExtractorPropertyMetadataFactoryTest.php b/src/Metadata/Tests/Property/Factory/ExtractorPropertyMetadataFactoryTest.php index ce35b84c66..875ceee29c 100644 --- a/src/Metadata/Tests/Property/Factory/ExtractorPropertyMetadataFactoryTest.php +++ b/src/Metadata/Tests/Property/Factory/ExtractorPropertyMetadataFactoryTest.php @@ -13,8 +13,10 @@ namespace ApiPlatform\Metadata\Tests\Property\Factory; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\Extractor\PropertyExtractorInterface; use ApiPlatform\Metadata\Property\Factory\ExtractorPropertyMetadataFactory; +use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface; use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\Comment; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; @@ -39,4 +41,40 @@ public function testCreateVirtualPropertyFromExtractor(): void self::assertSame('user.isAdmin() === true', $metadata->getSecurity()); } + + public function testCreateNativeTypeWithoutDecoratedFactory(): void + { + $extractor = $this->createStub(PropertyExtractorInterface::class); + $extractor->method('getProperties')->willReturn([ + Comment::class => [ + 'tags' => [ + 'nativeType' => 'list', + ], + ], + ]); + + $metadata = (new ExtractorPropertyMetadataFactory($extractor))->create(Comment::class, 'tags'); + + self::assertSame('list', (string) $metadata->getNativeType()); + } + + public function testCreateNativeTypeWithDecoratedFactory(): void + { + $extractor = $this->createStub(PropertyExtractorInterface::class); + $extractor->method('getProperties')->willReturn([ + Comment::class => [ + 'id' => [ + 'nativeType' => 'int', + ], + ], + ]); + + $decorated = $this->createStub(PropertyMetadataFactoryInterface::class); + $decorated->method('create')->willReturn(new ApiProperty(readable: true)); + + $metadata = (new ExtractorPropertyMetadataFactory($extractor, $decorated))->create(Comment::class, 'id'); + + self::assertSame('int', (string) $metadata->getNativeType()); + self::assertTrue($metadata->isReadable()); + } }