diff --git a/src/Metadata/Resource/Factory/OperationDefaultsTrait.php b/src/Metadata/Resource/Factory/OperationDefaultsTrait.php index d320bd41577..31214c83d2c 100644 --- a/src/Metadata/Resource/Factory/OperationDefaultsTrait.php +++ b/src/Metadata/Resource/Factory/OperationDefaultsTrait.php @@ -187,16 +187,28 @@ private function getOperationWithDefaults(ApiResource $resource, Operation $oper $operation = $operation->withName($operation->getRouteName()); } - $operationName = $operation->getName() ?? sprintf( - '_api_%s_%s%s', - $operation->getUriTemplate() ?: $operation->getShortName(), - strtolower($operation->getMethod() ?? HttpOperation::METHOD_GET), - $operation instanceof CollectionOperationInterface ? '_collection' : '', - ); + $path = ($operation->getRoutePrefix() ?? '').($operation->getUriTemplate() ?? ''); + $operationName = $operation->getName() ?? $this->getDefaultOperationName($operation, $resource->getClass()); return [ $operationName, $operation, ]; } + + private function getDefaultShortname(string $resourceClass): string + { + return (false !== $pos = strrpos($resourceClass, '\\')) ? substr($resourceClass, $pos + 1) : $resourceClass; + } + + private function getDefaultOperationName(HttpOperation $operation, string $resourceClass): string + { + $path = ($operation->getRoutePrefix() ?? '').($operation->getUriTemplate() ?? ''); + + return sprintf( + '_api_%s_%s%s', + $path ?: ($operation->getShortName() ?? $this->getDefaultShortname($resourceClass)), + strtolower($operation->getMethod() ?? HttpOperation::METHOD_GET), + $operation instanceof CollectionOperationInterface ? '_collection' : ''); + } } diff --git a/src/Metadata/Resource/Factory/OperationNameResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/OperationNameResourceMetadataCollectionFactory.php index 120c8a1c822..9127deaec85 100644 --- a/src/Metadata/Resource/Factory/OperationNameResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/OperationNameResourceMetadataCollectionFactory.php @@ -13,8 +13,6 @@ namespace ApiPlatform\Metadata\Resource\Factory; -use ApiPlatform\Metadata\CollectionOperationInterface; -use ApiPlatform\Metadata\HttpOperation; use ApiPlatform\Metadata\Resource\ResourceMetadataCollection; /** @@ -24,6 +22,8 @@ */ final class OperationNameResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface { + use OperationDefaultsTrait; + public function __construct(private readonly ?ResourceMetadataCollectionFactoryInterface $decorated = null) { } @@ -52,8 +52,7 @@ public function create(string $resourceClass): ResourceMetadataCollection continue; } - $path = ($operation->getRoutePrefix() ?? '').($operation->getUriTemplate() ?? ''); - $newOperationName = sprintf('_api_%s_%s%s', $path ?: ($operation->getShortName() ?? $this->getDefaultShortname($resourceClass)), strtolower($operation->getMethod() ?? HttpOperation::METHOD_GET), $operation instanceof CollectionOperationInterface ? '_collection' : ''); + $newOperationName = $this->getDefaultOperationName($operation, $resourceClass); $operations->remove($operationName)->add($newOperationName, $operation->withName($newOperationName)); } @@ -62,9 +61,4 @@ public function create(string $resourceClass): ResourceMetadataCollection return $resourceMetadataCollection; } - - private function getDefaultShortname(string $resourceClass): string - { - return (false !== $pos = strrpos($resourceClass, '\\')) ? substr($resourceClass, $pos + 1) : $resourceClass; - } } diff --git a/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php index 0c62bf81693..41f8064ae7a 100644 --- a/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php @@ -27,6 +27,8 @@ */ final class UriTemplateResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface { + use OperationDefaultsTrait; + private $triggerLegacyFormatOnce = []; public function __construct(private readonly LinkFactoryInterface $linkFactory, private readonly PathSegmentNameGeneratorInterface $pathSegmentNameGenerator, private readonly ?ResourceMetadataCollectionFactoryInterface $decorated = null) @@ -78,12 +80,12 @@ public function create(string $resourceClass): ResourceMetadataCollection } $operation = $operation->withUriTemplate($this->generateUriTemplate($operation)); - $operationName = $operation->getName() ?: sprintf('_api_%s_%s%s', $operation->getUriTemplate(), strtolower($operation->getMethod() ?? HttpOperation::METHOD_GET), $operation instanceof CollectionOperationInterface ? '_collection' : ''); + if (!$operation->getName()) { - $operation = $operation->withName($operationName); + $operation = $operation->withName($this->getDefaultOperationName($operation, $resourceClass)); } - $operations->add($operationName, $operation); + $operations->add($operation->getName(), $operation); } $resource = $resource->withOperations($operations->sort()); diff --git a/tests/Metadata/Extractor/ResourceMetadataCompatibilityTest.php b/tests/Metadata/Extractor/ResourceMetadataCompatibilityTest.php index 77e84dc5134..1d1957ea04e 100644 --- a/tests/Metadata/Extractor/ResourceMetadataCompatibilityTest.php +++ b/tests/Metadata/Extractor/ResourceMetadataCompatibilityTest.php @@ -14,7 +14,6 @@ namespace ApiPlatform\Tests\Metadata\Extractor; use ApiPlatform\Metadata\ApiResource; -use ApiPlatform\Metadata\CollectionOperationInterface; use ApiPlatform\Metadata\Delete; use ApiPlatform\Metadata\Extractor\XmlResourceExtractor; use ApiPlatform\Metadata\Extractor\YamlResourceExtractor; @@ -474,7 +473,7 @@ private function buildApiResources(): array // Build default operations $operations = []; foreach ([new Get(), new GetCollection(), new Post(), new Put(), new Patch(), new Delete()] as $operation) { - $operationName = sprintf('_api_%s_%s%s', $resource->getShortName(), strtolower($operation->getMethod()), $operation instanceof CollectionOperationInterface ? '_collection' : ''); + $operationName = $this->getDefaultOperationName($operation, self::RESOURCE_CLASS); [$name, $operation] = $this->getOperationWithDefaults($resource, $operation); $operations[$name] = $operation; } @@ -572,7 +571,7 @@ private function withOperations(array $values, ?array $fixtures): Operations throw new \RuntimeException(sprintf('Unknown Operation parameter "%s".', $parameter)); } - $operationName = $operation->getName() ?? sprintf('_api_%s_%s%s', $operation->getUriTemplate() ?: $operation->getShortName(), strtolower($operation->getMethod()), $operation instanceof CollectionOperationInterface ? '_collection' : ''); + $operationName = $operation->getName() ?? $this->getDefaultOperationName($operation, self::RESOURCE_CLASS); $operations[$operationName] = $operation; } diff --git a/tests/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactoryTest.php b/tests/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactoryTest.php index 22948b75d22..8b27b5d5b7a 100644 --- a/tests/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactoryTest.php +++ b/tests/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactoryTest.php @@ -107,6 +107,20 @@ class: AttributeResource::class, uriTemplate: '/dummy/{dummyId}/attribute_resources/{id}', uriVariables: ['dummyId' => ['from_class' => Dummy::class, 'identifiers' => ['id']], 'id' => ['from_class' => AttributeResource::class, 'identifiers' => ['id']]], ), + new ApiResource( + shortName: 'AttributeResource', + class: AttributeResource::class, + uriVariables: ['id' => new Link(fromClass: AttributeResource::class, identifiers: ['id'])], + operations: [ + new Get( + shortName: 'AttributeResource', + class: AttributeResource::class, + controller: 'api_platform.action.placeholder', + uriVariables: ['id' => new Link(fromClass: AttributeResource::class, identifiers: ['id'])], + routePrefix: '/prefix', + ), + ] + ), ]), ); @@ -169,6 +183,21 @@ class: AttributeResource::class, uriVariables: ['dummyId' => new Link(fromClass: Dummy::class, identifiers: ['id'], parameterName: 'dummyId'), 'id' => new Link(fromClass: AttributeResource::class, identifiers: ['id'], parameterName: 'id')], operations: [], ), + new ApiResource( + uriVariables: ['id' => new Link(fromClass: AttributeResource::class, identifiers: ['id'], parameterName: 'id')], + shortName: 'AttributeResource', + class: AttributeResource::class, + operations: [ + '_api_/prefix/attribute_resources/{id}{._format}_get' => new Get( + uriTemplate: '/attribute_resources/{id}{._format}', + shortName: 'AttributeResource', + class: AttributeResource::class, + controller: 'api_platform.action.placeholder', + uriVariables: ['id' => new Link(fromClass: AttributeResource::class, identifiers: ['id'], parameterName: 'id')], + routePrefix: '/prefix', + name: '_api_/prefix/attribute_resources/{id}{._format}_get'), + ] + ), ]), $uriTemplateResourceMetadataCollectionFactory->create(AttributeResource::class) );