. */ declare(strict_types=1); namespace App\ApiPlatform\DocumentedAPIProperties; use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; use ApiPlatform\Metadata\Property\PropertyNameCollection; use ReflectionClass; use Symfony\Component\DependencyInjection\Attribute\AsDecorator; /** * This decorator adds the virtual property names defined by the DocumentedAPIProperty attribute to the property name collection * which then get picked up by the openapi schema generator */ #[AsDecorator('api_platform.metadata.property.name_collection_factory')] class PropertyNameCollectionFactory implements PropertyNameCollectionFactoryInterface { public function __construct(private readonly PropertyNameCollectionFactoryInterface $decorated) { } public function create(string $resourceClass, array $options = []): PropertyNameCollection { // Get the default properties from the decorated service $propertyNames = $this->decorated->create($resourceClass, $options); //Only become active in the context of the openapi schema generation if (!isset($options['schema_type'])) { return $propertyNames; } if (!class_exists($resourceClass)) { return $propertyNames; } $properties = iterator_to_array($propertyNames); $refClass = new ReflectionClass($resourceClass); foreach ($refClass->getAttributes(DocumentedAPIProperty::class) as $attribute) { /** @var DocumentedAPIProperty $instance */ $instance = $attribute->newInstance(); $properties[] = $instance->property; } return new PropertyNameCollection($properties); } }