diff --git a/src/JsonLd/ContextBuilder.php b/src/JsonLd/ContextBuilder.php index a3d638f5a7b..36dc391350f 100644 --- a/src/JsonLd/ContextBuilder.php +++ b/src/JsonLd/ContextBuilder.php @@ -115,14 +115,15 @@ public function getResourceContextUri(string $resourceClass, ?int $referenceType public function getAnonymousResourceContext(object $object, array $context = [], int $referenceType = UrlGeneratorInterface::ABS_PATH): array { $outputClass = $this->getObjectClass($object); - $operation = $context['operation'] ?? new Get(shortName: (new \ReflectionClass($outputClass))->getShortName()); + $operation = $context['operation'] ?? new Get(shortName: (new \ReflectionClass($outputClass))->getShortName(), normalizationContext: [self::HYDRA_CONTEXT_HAS_PREFIX => $context[self::HYDRA_CONTEXT_HAS_PREFIX] ?? $this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true]); $shortName = $operation->getShortName(); $jsonLdContext = [ '@context' => $this->getResourceContextWithShortname( $outputClass, $referenceType, - $shortName + $shortName, + $operation ), '@type' => $shortName, ]; @@ -148,6 +149,7 @@ public function getAnonymousResourceContext(object $object, array $context = [], private function getResourceContextWithShortname(string $resourceClass, int $referenceType, string $shortName, ?HttpOperation $operation = null): array { $context = $this->getBaseContext($referenceType); + $hydraPrefix = $operation->getNormalizationContext()[self::HYDRA_CONTEXT_HAS_PREFIX] ?? $this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true; $propertyContext = $operation ? ['normalization_groups' => $operation->getNormalizationContext()['groups'] ?? null, 'denormalization_groups' => $operation->getDenormalizationContext()['groups'] ?? null] : ['normalization_groups' => [], 'denormalization_groups' => []]; foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $propertyName) { @@ -184,10 +186,6 @@ private function getResourceContextWithShortname(string $resourceClass, int $ref } } - if (false === ($this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true)) { - return [HYDRA_CONTEXT, $context]; - } - return $context; } } diff --git a/src/JsonLd/Serializer/JsonLdContextTrait.php b/src/JsonLd/Serializer/JsonLdContextTrait.php index 2e0cef8a402..7b2d98b2cc7 100644 --- a/src/JsonLd/Serializer/JsonLdContextTrait.php +++ b/src/JsonLd/Serializer/JsonLdContextTrait.php @@ -14,6 +14,7 @@ namespace ApiPlatform\JsonLd\Serializer; use ApiPlatform\JsonLd\AnonymousContextBuilderInterface; +use ApiPlatform\JsonLd\ContextBuilder; use ApiPlatform\JsonLd\ContextBuilderInterface; /** @@ -49,13 +50,19 @@ private function addJsonLdContext(ContextBuilderInterface $contextBuilder, strin private function createJsonLdContext(AnonymousContextBuilderInterface $contextBuilder, $object, array &$context): array { + $anonymousContext = ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null]; + + if (isset($context[ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX])) { + $anonymousContext[ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX] = $context[ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX]; + } + // We're in a collection, don't add the @context part if (isset($context['jsonld_has_context'])) { - return $contextBuilder->getAnonymousResourceContext($object, ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null, 'has_context' => true]); + return $contextBuilder->getAnonymousResourceContext($object, ['has_context' => true] + $anonymousContext); } $context['jsonld_has_context'] = true; - return $contextBuilder->getAnonymousResourceContext($object, ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null]); + return $contextBuilder->getAnonymousResourceContext($object, $anonymousContext); } } diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue6810/JsonLdContextOutput.php b/tests/Fixtures/TestBundle/ApiResource/Issue6810/JsonLdContextOutput.php new file mode 100644 index 00000000000..fe1ee0d5548 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue6810/JsonLdContextOutput.php @@ -0,0 +1,30 @@ + + * + * 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\Issue6810; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Operation; + +#[Get('/json_ld_context_output', provider: [self::class, 'getData'], output: Output::class, normalizationContext: ['hydra_prefix' => false])] +class JsonLdContextOutput +{ + public function __construct(public string $id) + { + } + + public static function getData(Operation $operation, array $uriVariables = [], array $context = []): Output + { + return new Output(); + } +} diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue6810/Output.php b/tests/Fixtures/TestBundle/ApiResource/Issue6810/Output.php new file mode 100644 index 00000000000..0a3a8d89ab4 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue6810/Output.php @@ -0,0 +1,19 @@ + + * + * 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\Issue6810; + +class Output +{ + public string $foo = 'bar'; +} diff --git a/tests/Functional/JsonLd.php b/tests/Functional/JsonLd.php index da76c723e76..b57b8ea1338 100644 --- a/tests/Functional/JsonLd.php +++ b/tests/Functional/JsonLd.php @@ -14,13 +14,25 @@ namespace ApiPlatform\Tests\Functional; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; +use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6810\JsonLdContextOutput; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Bar; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Foo; +use ApiPlatform\Tests\SetupClassResourcesTrait; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Tools\SchemaTool; class JsonLd extends ApiTestCase { + use SetupClassResourcesTrait; + + /** + * @return class-string[] + */ + public static function getResources(): array + { + return [JsonLdContextOutput::class]; + } + /** * The input DTO denormalizes an existing Doctrine entity. */ @@ -39,6 +51,20 @@ public function testIssue6465(): void $this->assertEquals('Bar two', $res['title']); } + public function testContextWithOutput(): void + { + $response = self::createClient()->request( + 'GET', + '/json_ld_context_output', + ); + $res = $response->toArray(); + $this->assertEquals($res['@context'], [ + '@vocab' => 'http://localhost/docs.jsonld#', + 'hydra' => 'http://www.w3.org/ns/hydra/core#', + 'foo' => 'Output/foo', + ]); + } + protected function setUp(): void { self::bootKernel(); @@ -55,8 +81,12 @@ protected function setUp(): void $classes[] = $manager->getClassMetadata($entityClass); } - $schemaTool = new SchemaTool($manager); - @$schemaTool->createSchema($classes); + try { + $schemaTool = new SchemaTool($manager); + @$schemaTool->createSchema($classes); + } catch (\Exception $e) { + return; + } $foo = new Foo(); $foo->title = 'Foo';