diff --git a/src/ApiPlatform/NormalizePropertyNameCollectionFactory.php b/src/ApiPlatform/NormalizePropertyNameCollectionFactory.php new file mode 100644 index 00000000..33a4ef90 --- /dev/null +++ b/src/ApiPlatform/NormalizePropertyNameCollectionFactory.php @@ -0,0 +1,77 @@ +. + */ + +declare(strict_types=1); + + +namespace App\ApiPlatform; + +use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; +use ApiPlatform\Metadata\Property\PropertyNameCollection; +use Symfony\Component\DependencyInjection\Attribute\AsDecorator; +use function Symfony\Component\String\u; + +/** + * This decorator removes all camelCase property names from the property name collection, if a snake_case version exists. + * This is a fix for https://github.com/Part-DB/Part-DB-server/issues/862, as the openapi schema generator wrongly collects + * both camelCase and snake_case property names, which leads to duplicate properties in the schema. + * This seems to come from the fact that the openapi schema generator uses no serializerContext, which seems then to collect + * the getters too... + */ +#[AsDecorator('api_platform.metadata.property.name_collection_factory')] +class NormalizePropertyNameCollectionFactory 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 we are not in the jsonapi generator (which sets no serializer groups), return the property names as is + if (isset($options['serializer_groups'])) { + return $propertyNames; + } + + //Remove all camelCase property names from the collection, if a snake_case version exists + $properties = iterator_to_array($propertyNames); + + foreach ($properties as $property) { + if (str_contains($property, '_')) { + $camelized = u($property)->camel()->toString(); + + //If the camelized version exists, remove it from the collection + $index = array_search($camelized, $properties); + if ($index !== false) { + unset($properties[$index]); + } + } + } + + return new PropertyNameCollection($properties); + } +} \ No newline at end of file