diff --git a/phpunit.baseline.xml b/phpunit.baseline.xml
index 49cbb1c2b4..cd96ca6d32 100644
--- a/phpunit.baseline.xml
+++ b/phpunit.baseline.xml
@@ -30,4 +30,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Hydra/Serializer/DocumentationNormalizer.php b/src/Hydra/Serializer/DocumentationNormalizer.php
index 5130557e36..11f6d20c64 100644
--- a/src/Hydra/Serializer/DocumentationNormalizer.php
+++ b/src/Hydra/Serializer/DocumentationNormalizer.php
@@ -26,7 +26,6 @@
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
-use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
use ApiPlatform\Metadata\ResourceClassResolverInterface;
use ApiPlatform\Metadata\UrlGeneratorInterface;
use ApiPlatform\Metadata\Util\TypeHelper;
@@ -76,16 +75,17 @@ public function normalize(mixed $data, ?string $format = null, array $context =
foreach ($data->getResourceNameCollection() as $resourceClass) {
$resourceMetadataCollection = $this->resourceMetadataFactory->create($resourceClass);
- $resourceMetadata = $resourceMetadataCollection[0];
- if (true === $resourceMetadata->getHideHydraOperation()) {
- continue;
- }
+ foreach ($resourceMetadataCollection as $resourceMetadata) {
+ if (true === $resourceMetadata->getHideHydraOperation()) {
+ continue;
+ }
- $shortName = $resourceMetadata->getShortName();
- $prefixedShortName = $resourceMetadata->getTypes()[0] ?? "#$shortName";
+ $shortName = $resourceMetadata->getShortName();
+ $prefixedShortName = $resourceMetadata->getTypes()[0] ?? "#$shortName";
- $this->populateEntrypointProperties($resourceMetadata, $shortName, $prefixedShortName, $entrypointProperties, $hydraPrefix, $resourceMetadataCollection);
- $classes[] = $this->getClass($resourceClass, $resourceMetadata, $shortName, $prefixedShortName, $context, $hydraPrefix, $resourceMetadataCollection);
+ $this->populateEntrypointProperties($resourceMetadata, $shortName, $prefixedShortName, $entrypointProperties, $hydraPrefix);
+ $classes[] = $this->getClass($resourceClass, $resourceMetadata, $shortName, $prefixedShortName, $context, $hydraPrefix);
+ }
}
return $this->computeDoc($data, $this->getClasses($entrypointProperties, $classes, $hydraPrefix), $hydraPrefix);
@@ -94,9 +94,9 @@ public function normalize(mixed $data, ?string $format = null, array $context =
/**
* Populates entrypoint properties.
*/
- private function populateEntrypointProperties(ApiResource $resourceMetadata, string $shortName, string $prefixedShortName, array &$entrypointProperties, string $hydraPrefix, ?ResourceMetadataCollection $resourceMetadataCollection = null): void
+ private function populateEntrypointProperties(ApiResource $resourceMetadata, string $shortName, string $prefixedShortName, array &$entrypointProperties, string $hydraPrefix): void
{
- $hydraCollectionOperations = $this->getHydraOperations(true, $resourceMetadataCollection, $hydraPrefix);
+ $hydraCollectionOperations = $this->getHydraOperations(true, $resourceMetadata, $hydraPrefix);
if (empty($hydraCollectionOperations)) {
return;
}
@@ -135,7 +135,7 @@ private function populateEntrypointProperties(ApiResource $resourceMetadata, str
/**
* Gets a Hydra class.
*/
- private function getClass(string $resourceClass, ApiResource $resourceMetadata, string $shortName, string $prefixedShortName, array $context, string $hydraPrefix, ?ResourceMetadataCollection $resourceMetadataCollection = null): array
+ private function getClass(string $resourceClass, ApiResource $resourceMetadata, string $shortName, string $prefixedShortName, array $context, string $hydraPrefix): array
{
$description = $resourceMetadata->getDescription();
$isDeprecated = $resourceMetadata->getDeprecationReason();
@@ -145,7 +145,7 @@ private function getClass(string $resourceClass, ApiResource $resourceMetadata,
'@type' => $hydraPrefix.'Class',
$hydraPrefix.'title' => $shortName,
$hydraPrefix.'supportedProperty' => $this->getHydraProperties($resourceClass, $resourceMetadata, $shortName, $prefixedShortName, $context, $hydraPrefix),
- $hydraPrefix.'supportedOperation' => $this->getHydraOperations(false, $resourceMetadataCollection, $hydraPrefix),
+ $hydraPrefix.'supportedOperation' => $this->getHydraOperations(false, $resourceMetadata, $hydraPrefix),
];
if (null !== $description) {
@@ -252,21 +252,19 @@ private function getHydraProperties(string $resourceClass, ApiResource $resource
/**
* Gets Hydra operations.
*/
- private function getHydraOperations(bool $collection, ?ResourceMetadataCollection $resourceMetadataCollection = null, string $hydraPrefix = ContextBuilder::HYDRA_PREFIX): array
+ private function getHydraOperations(bool $collection, ApiResource $resourceMetadata, string $hydraPrefix = ContextBuilder::HYDRA_PREFIX): array
{
$hydraOperations = [];
- foreach ($resourceMetadataCollection as $resourceMetadata) {
- foreach ($resourceMetadata->getOperations() as $operation) {
- if (true === $operation->getHideHydraOperation()) {
- continue;
- }
-
- if (('POST' === $operation->getMethod() || $operation instanceof CollectionOperationInterface) !== $collection) {
- continue;
- }
+ foreach ($resourceMetadata->getOperations() as $operation) {
+ if (true === $operation->getHideHydraOperation()) {
+ continue;
+ }
- $hydraOperations[] = $this->getHydraOperation($operation, $operation->getShortName(), $hydraPrefix);
+ if (('POST' === $operation->getMethod() || $operation instanceof CollectionOperationInterface) !== $collection) {
+ continue;
}
+
+ $hydraOperations[] = $this->getHydraOperation($operation, $operation->getShortName(), $hydraPrefix);
}
return $hydraOperations;
diff --git a/src/Hydra/Serializer/EntrypointNormalizer.php b/src/Hydra/Serializer/EntrypointNormalizer.php
index 8ff3327b6f..5b2cdee4ec 100644
--- a/src/Hydra/Serializer/EntrypointNormalizer.php
+++ b/src/Hydra/Serializer/EntrypointNormalizer.php
@@ -50,10 +50,6 @@ public function normalize(mixed $data, ?string $format = null, array $context =
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
foreach ($resourceMetadata as $resource) {
- if ($resource->getExtraProperties()['is_alternate_resource_metadata'] ?? false) {
- continue;
- }
-
foreach ($resource->getOperations() as $operation) {
$key = lcfirst($resource->getShortName());
if (true === $operation->getHideHydraOperation() || !$operation instanceof CollectionOperationInterface || isset($entrypoint[$key])) {
diff --git a/src/Hydra/Tests/Serializer/DocumentationNormalizerTest.php b/src/Hydra/Tests/Serializer/DocumentationNormalizerTest.php
index d04c92cb3b..71b9334125 100644
--- a/src/Hydra/Tests/Serializer/DocumentationNormalizerTest.php
+++ b/src/Hydra/Tests/Serializer/DocumentationNormalizerTest.php
@@ -235,6 +235,88 @@ private function doTestNormalize($resourceMetadataFactory = null): void
'hydra:description' => 'Replaces the dummy resource.',
'returns' => 'dummy',
],
+ ],
+ ],
+ [
+ '@id' => '#relatedDummy',
+ '@type' => 'hydra:Class',
+ 'hydra:title' => 'relatedDummy',
+ 'hydra:supportedProperty' => [
+ [
+ '@type' => 'hydra:SupportedProperty',
+ 'hydra:property' => [
+ '@id' => '#relatedDummy/name',
+ '@type' => 'rdf:Property',
+ 'label' => 'name',
+ 'domain' => '#relatedDummy',
+ 'range' => 'xsd:string',
+ ],
+ 'hydra:title' => 'name',
+ 'hydra:required' => false,
+ 'hydra:readable' => true,
+ 'hydra:writeable' => true,
+ 'hydra:description' => 'name',
+ ],
+ [
+ '@type' => 'hydra:SupportedProperty',
+ 'hydra:property' => [
+ '@id' => '#relatedDummy/description',
+ '@type' => 'rdf:Property',
+ 'label' => 'description',
+ 'domain' => '#relatedDummy',
+ 'range' => '@id',
+ ],
+ 'hydra:title' => 'description',
+ 'hydra:required' => false,
+ 'hydra:readable' => true,
+ 'hydra:writeable' => true,
+ 'hydra:description' => 'description',
+ ],
+ [
+ '@type' => 'hydra:SupportedProperty',
+ 'hydra:property' => [
+ '@id' => '#relatedDummy/name_converted',
+ '@type' => 'rdf:Property',
+ 'label' => 'name_converted',
+ 'domain' => '#relatedDummy',
+ 'range' => 'xsd:string',
+ ],
+ 'hydra:title' => 'name_converted',
+ 'hydra:required' => false,
+ 'hydra:readable' => true,
+ 'hydra:writeable' => true,
+ 'hydra:description' => 'name converted',
+ ],
+ [
+ '@type' => 'hydra:SupportedProperty',
+ 'hydra:property' => [
+ '@id' => '#relatedDummy/relatedDummy',
+ '@type' => 'rdf:Property',
+ 'label' => 'relatedDummy',
+ 'domain' => '#relatedDummy',
+ 'range' => '#relatedDummy',
+ ],
+ 'hydra:title' => 'relatedDummy',
+ 'hydra:required' => false,
+ 'hydra:readable' => true,
+ 'hydra:writeable' => true,
+ 'hydra:description' => 'This is a name.',
+ ],
+ [
+ '@type' => 'hydra:SupportedProperty',
+ 'hydra:property' => [
+ '@id' => 'https://schema.org/Dummy',
+ '@type' => 'rdf:Property',
+ 'label' => 'iri',
+ 'domain' => '#relatedDummy',
+ ],
+ 'hydra:title' => 'iri',
+ 'hydra:required' => null,
+ 'hydra:readable' => null,
+ 'hydra:writeable' => false,
+ ],
+ ],
+ 'hydra:supportedOperation' => [
[
'@type' => ['hydra:Operation', 'schema:FindAction'],
'hydra:method' => 'GET',
@@ -722,6 +804,88 @@ public function testNormalizeWithoutPrefix(): void
'description' => 'Replaces the dummy resource.',
'returns' => 'dummy',
],
+ ],
+ ],
+ [
+ '@id' => '#relatedDummy',
+ '@type' => 'Class',
+ 'title' => 'relatedDummy',
+ 'supportedProperty' => [
+ [
+ '@type' => 'SupportedProperty',
+ 'property' => [
+ '@id' => '#relatedDummy/name',
+ '@type' => 'rdf:Property',
+ 'label' => 'name',
+ 'domain' => '#relatedDummy',
+ 'range' => 'xsd:string',
+ ],
+ 'title' => 'name',
+ 'required' => false,
+ 'readable' => true,
+ 'writeable' => true,
+ 'description' => 'name',
+ ],
+ [
+ '@type' => 'SupportedProperty',
+ 'property' => [
+ '@id' => '#relatedDummy/description',
+ '@type' => 'rdf:Property',
+ 'label' => 'description',
+ 'domain' => '#relatedDummy',
+ 'range' => '@id',
+ ],
+ 'title' => 'description',
+ 'required' => false,
+ 'readable' => true,
+ 'writeable' => true,
+ 'description' => 'description',
+ ],
+ [
+ '@type' => 'SupportedProperty',
+ 'property' => [
+ '@id' => '#relatedDummy/name_converted',
+ '@type' => 'rdf:Property',
+ 'label' => 'name_converted',
+ 'domain' => '#relatedDummy',
+ 'range' => 'xsd:string',
+ ],
+ 'title' => 'name_converted',
+ 'required' => false,
+ 'readable' => true,
+ 'writeable' => true,
+ 'description' => 'name converted',
+ ],
+ [
+ '@type' => 'SupportedProperty',
+ 'property' => [
+ '@id' => '#relatedDummy/relatedDummy',
+ '@type' => 'rdf:Property',
+ 'label' => 'relatedDummy',
+ 'domain' => '#relatedDummy',
+ 'range' => '#relatedDummy',
+ ],
+ 'title' => 'relatedDummy',
+ 'required' => false,
+ 'readable' => true,
+ 'writeable' => true,
+ 'description' => 'This is a name.',
+ ],
+ [
+ '@type' => 'SupportedProperty',
+ 'property' => [
+ '@id' => 'https://schema.org/Dummy',
+ '@type' => 'rdf:Property',
+ 'label' => 'iri',
+ 'domain' => '#relatedDummy',
+ ],
+ 'title' => 'iri',
+ 'required' => null,
+ 'readable' => null,
+ 'writeable' => false,
+ ],
+ ],
+ 'supportedOperation' => [
[
'@type' => ['Operation', 'schema:FindAction'],
'method' => 'GET',
diff --git a/src/JsonLd/ContextBuilder.php b/src/JsonLd/ContextBuilder.php
index 594dc68f5e..af693498a5 100644
--- a/src/JsonLd/ContextBuilder.php
+++ b/src/JsonLd/ContextBuilder.php
@@ -62,13 +62,14 @@ public function getEntrypointContext(int $referenceType = UrlGeneratorInterface:
$context = $this->getBaseContext($referenceType);
foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) {
- $shortName = $this->resourceMetadataFactory->create($resourceClass)[0]->getShortName();
- $resourceName = lcfirst($shortName);
+ foreach ($this->resourceMetadataFactory->create($resourceClass) as $resource) {
+ $resourceName = lcfirst($resource->getShortName());
- $context[$resourceName] = [
- '@id' => 'Entrypoint/'.$resourceName,
- '@type' => '@id',
- ];
+ $context[$resourceName] = [
+ '@id' => 'Entrypoint/'.$resourceName,
+ '@type' => '@id',
+ ];
+ }
}
return $context;
diff --git a/src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php b/src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php
index 914f22662b..4afe72a63f 100644
--- a/src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php
+++ b/src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php
@@ -202,7 +202,7 @@ private function buildResourceOperations(array $metadataCollection, string $reso
$resources[$index] = $resources[$index]->withGraphQlOperations($graphQlOperationsWithDefaults);
}
- return $resources;
+ return $this->deduplicateShortNames($resources);
}
/**
@@ -224,6 +224,52 @@ private function hasSameOperation(ApiResource $resource, string $operationClass,
return false;
}
+ /**
+ * When multiple ApiResource declarations on the same class share the same shortName,
+ * suffix duplicates with an incrementing number (e.g. Book, Book2, Book3).
+ *
+ * @param ApiResource[] $resources
+ *
+ * @return ApiResource[]
+ */
+ private function deduplicateShortNames(array $resources): array
+ {
+ $enabled = $this->defaults['extra_properties']['deduplicate_resource_short_names'] ?? false;
+ $shortNameCounts = [];
+
+ foreach ($resources as $index => $resource) {
+ $shortName = $resource->getShortName();
+ if (!isset($shortNameCounts[$shortName])) {
+ $shortNameCounts[$shortName] = 1;
+ continue;
+ }
+
+ if (!$enabled) {
+ if (1 === $shortNameCounts[$shortName]) {
+ trigger_deprecation('api-platform/core', '4.2', 'Having multiple "#[ApiResource]" attributes with the same "shortName" "%s" on class "%s" is deprecated and will result in automatic short name deduplication in API Platform 5.x. Set "defaults.extra_properties.deduplicate_resource_short_names" to "true" in the API Platform configuration to enable it now.', $shortName, $resource->getClass());
+ }
+ ++$shortNameCounts[$shortName];
+ continue;
+ }
+
+ $newShortName = $shortName.(++$shortNameCounts[$shortName]);
+ $resource = $resource->withShortName($newShortName);
+
+ // Update operations to reflect the new shortName
+ if ($operations = $resource->getOperations()) {
+ $updatedOperations = [];
+ foreach ($operations as $key => $operation) {
+ $updatedOperations[$key] = $operation->withShortName($newShortName);
+ }
+ $resource = $resource->withOperations(new Operations($updatedOperations));
+ }
+
+ $resources[$index] = $resource;
+ }
+
+ return $resources;
+ }
+
/**
* @template T of Metadata
*
diff --git a/src/Metadata/Tests/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php b/src/Metadata/Tests/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php
index b732c16b25..3f744979a0 100644
--- a/src/Metadata/Tests/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php
+++ b/src/Metadata/Tests/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php
@@ -268,6 +268,40 @@ public function testNameDeclarationShouldNotBeRemoved(): void
$this->assertTrue($operations->has('password_reset'));
}
+ public function testDeduplicateShortNamesWhenEnabled(): void
+ {
+ $factory = new AttributesResourceMetadataCollectionFactory(defaults: [
+ 'extra_properties' => ['deduplicate_resource_short_names' => true],
+ ], graphQlEnabled: true);
+
+ $collection = $factory->create(AttributeResource::class);
+
+ // First resource keeps original shortName
+ $this->assertSame('AttributeResource', $collection[0]->getShortName());
+
+ // Second resource gets deduplicated shortName
+ $this->assertSame('AttributeResource2', $collection[1]->getShortName());
+
+ // Operations on the second resource also get the deduplicated shortName
+ foreach ($collection[1]->getOperations() as $operation) {
+ $this->assertSame('AttributeResource2', $operation->getShortName());
+ }
+ }
+
+ /** @group legacy */
+ public function testDeduplicateShortNamesTriggersDeprecationWhenDisabled(): void
+ {
+ $factory = new AttributesResourceMetadataCollectionFactory(graphQlEnabled: true);
+
+ $this->expectUserDeprecationMessage('Since api-platform/core 4.2: Having multiple "#[ApiResource]" attributes with the same "shortName" "AttributeResource" on class "ApiPlatform\Metadata\Tests\Fixtures\ApiResource\AttributeResource" is deprecated and will result in automatic short name deduplication in API Platform 5.x. Set "defaults.extra_properties.deduplicate_resource_short_names" to "true" in the API Platform configuration to enable it now.');
+
+ $collection = $factory->create(AttributeResource::class);
+
+ // Without the flag, shortNames are NOT deduplicated
+ $this->assertSame('AttributeResource', $collection[0]->getShortName());
+ $this->assertSame('AttributeResource', $collection[1]->getShortName());
+ }
+
public function testWithParameters(): void
{
$attributeResourceMetadataCollectionFactory = new AttributesResourceMetadataCollectionFactory();
diff --git a/src/Metadata/phpunit.baseline.xml b/src/Metadata/phpunit.baseline.xml
index b0766d7d30..d4629cdfe6 100644
--- a/src/Metadata/phpunit.baseline.xml
+++ b/src/Metadata/phpunit.baseline.xml
@@ -5,4 +5,10 @@
+
+
+
+
+
+
diff --git a/tests/Fixtures/TestBundle/ApiResource/MultipleResourceBook.php b/tests/Fixtures/TestBundle/ApiResource/MultipleResourceBook.php
new file mode 100644
index 0000000000..a1c70f0a7e
--- /dev/null
+++ b/tests/Fixtures/TestBundle/ApiResource/MultipleResourceBook.php
@@ -0,0 +1,41 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource;
+
+use ApiPlatform\Metadata\ApiProperty;
+use ApiPlatform\Metadata\ApiResource;
+
+#[ApiResource(
+ uriTemplate: '/admin/multi_route_books',
+)]
+#[ApiResource(
+ shortName: 'MultipleResourceBook2',
+ uriTemplate: '/multi_route_books',
+)]
+class MultipleResourceBook
+{
+ #[ApiProperty(identifier: true)]
+ public int $id;
+
+ public string $title;
+
+ public string $isbn;
+
+ public function __construct(int $id = 0, string $title = '', string $isbn = '')
+ {
+ $this->id = $id;
+ $this->title = $title;
+ $this->isbn = $isbn;
+ }
+}
diff --git a/tests/Functional/MultipleResourceEntrypointTest.php b/tests/Functional/MultipleResourceEntrypointTest.php
new file mode 100644
index 0000000000..0277946dee
--- /dev/null
+++ b/tests/Functional/MultipleResourceEntrypointTest.php
@@ -0,0 +1,121 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace ApiPlatform\Tests\Functional;
+
+use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
+use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\MultipleResourceBook;
+use ApiPlatform\Tests\SetupClassResourcesTrait;
+
+/**
+ * Functional test for entrypoint with multiple ApiResource declarations.
+ *
+ * Tests that when a resource has multiple #[ApiResource] attributes,
+ * both are properly exposed in the entrypoint, context, and documentation,
+ * and that duplicate shortNames are suffixed with a number.
+ */
+class MultipleResourceEntrypointTest extends ApiTestCase
+{
+ use SetupClassResourcesTrait;
+
+ protected static ?bool $alwaysBootKernel = false;
+
+ /**
+ * @return class-string[]
+ */
+ public static function getResources(): array
+ {
+ return [MultipleResourceBook::class];
+ }
+
+ /**
+ * Test that /contexts/Entrypoint exposes both resource shortNames.
+ *
+ * The first resource keeps the class shortName (MultipleResourceBook),
+ * the second is suffixed (MultipleResourceBook2).
+ */
+ public function testEntrypointContextExposesMultipleResources(): void
+ {
+ $response = self::createClient()->request('GET', '/contexts/Entrypoint', [
+ 'headers' => ['accept' => 'application/ld+json'],
+ ]);
+
+ $this->assertResponseIsSuccessful();
+ $data = $response->toArray();
+
+ $this->assertArrayHasKey('@context', $data);
+ $context = $data['@context'];
+
+ $this->assertArrayHasKey('multipleResourceBook', $context);
+ $this->assertIsArray($context['multipleResourceBook']);
+ $this->assertEquals('Entrypoint/multipleResourceBook', $context['multipleResourceBook']['@id']);
+ $this->assertEquals('@id', $context['multipleResourceBook']['@type']);
+
+ $this->assertArrayHasKey('multipleResourceBook2', $context);
+ $this->assertIsArray($context['multipleResourceBook2']);
+ $this->assertEquals('Entrypoint/multipleResourceBook2', $context['multipleResourceBook2']['@id']);
+ $this->assertEquals('@id', $context['multipleResourceBook2']['@type']);
+ }
+
+ /**
+ * Test that /index.jsonld (the entrypoint) exposes both routes.
+ */
+ public function testEntrypointExposesMultipleRoutes(): void
+ {
+ $response = self::createClient()->request('GET', '/index.jsonld', [
+ 'headers' => ['accept' => 'application/ld+json'],
+ ]);
+
+ $this->assertResponseIsSuccessful();
+ $data = $response->toArray();
+
+ $this->assertArrayHasKey('multipleResourceBook', $data);
+ $this->assertEquals('/admin/multi_route_books', $data['multipleResourceBook']);
+
+ $this->assertArrayHasKey('multipleResourceBook2', $data);
+ $this->assertEquals('/multi_route_books', $data['multipleResourceBook2']);
+ }
+
+ /**
+ * Test that /docs.jsonld documents both resources as supported classes.
+ */
+ public function testDocumentationExposesMultipleResourcesAsSupportedClasses(): void
+ {
+ $response = self::createClient()->request('GET', '/docs.jsonld', [
+ 'headers' => ['accept' => 'application/ld+json'],
+ ]);
+
+ $this->assertResponseIsSuccessful();
+ $data = $response->toArray();
+
+ $this->assertArrayHasKey('hydra:supportedClass', $data);
+ $supportedClasses = $data['hydra:supportedClass'];
+
+ $firstResourceFound = false;
+ $secondResourceFound = false;
+
+ foreach ($supportedClasses as $supportedClass) {
+ if (isset($supportedClass['hydra:title']) && 'MultipleResourceBook' === $supportedClass['hydra:title']) {
+ $firstResourceFound = true;
+ $this->assertArrayHasKey('hydra:supportedOperation', $supportedClass);
+ }
+ if (isset($supportedClass['hydra:title']) && 'MultipleResourceBook2' === $supportedClass['hydra:title']) {
+ $secondResourceFound = true;
+ $this->assertArrayHasKey('hydra:supportedOperation', $supportedClass);
+ }
+ }
+
+ $this->assertTrue($firstResourceFound, 'MultipleResourceBook should be in hydra:supportedClass');
+ $this->assertTrue($secondResourceFound, 'MultipleResourceBook2 should be in hydra:supportedClass');
+ }
+}
diff --git a/tests/Symfony/Bundle/Command/DebugResourceCommandTest.php b/tests/Symfony/Bundle/Command/DebugResourceCommandTest.php
index 92006a89c1..7f512bc735 100644
--- a/tests/Symfony/Bundle/Command/DebugResourceCommandTest.php
+++ b/tests/Symfony/Bundle/Command/DebugResourceCommandTest.php
@@ -48,6 +48,7 @@ private function getCommandTester(?DataDumperInterface $dumper = null): CommandT
return new CommandTester($command);
}
+ /** @group legacy */
public function testDebugResource(): void
{
$varDumper = $this->prophesize(DataDumperInterface::class);
@@ -61,6 +62,7 @@ public function testDebugResource(): void
$this->assertStringContainsString('Successfully dumped the selected resource', $commandTester->getDisplay());
}
+ /** @group legacy */
public function testDebugOperation(): void
{
$varDumper = $this->prophesize(DataDumperInterface::class);