Skip to content

Commit 3ab176e

Browse files
committed
fix(symfony): include value-object transformers in JSON-LD streamer locator
Symfony 8.1 split json_streamer.value_transformer into property_value_transformer and value_object_transformer. The custom JSON-LD JsonStreamReader/Writer bypassed Symfony's TransformerPass, so DateTimeInterface (and other value objects) were missing from its locator — making the generator emit a Splitter::splitDict call on date-time strings ("JSON is not valid").
1 parent ad027fb commit 3ab176e

3 files changed

Lines changed: 50 additions & 0 deletions

File tree

src/Symfony/Bundle/ApiPlatformBundle.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\FilterPass;
2222
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\GraphQlResolverPass;
2323
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\GraphQlTypePass;
24+
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\JsonStreamerTransformerPass;
2425
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\MetadataAwareNameConverterPass;
2526
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\MutatorPass;
2627
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\SerializerMappingLoaderPass;
@@ -59,5 +60,7 @@ public function build(ContainerBuilder $container): void
5960
$container->addCompilerPass(new AuthenticatorManagerPass());
6061
$container->addCompilerPass(new SerializerMappingLoaderPass());
6162
$container->addCompilerPass(new MutatorPass());
63+
// Must run after Symfony's TransformerPass so we can rely on the value_object_transformer tag being processed.
64+
$container->addCompilerPass(new JsonStreamerTransformerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -10);
6265
}
6366
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler;
15+
16+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17+
use Symfony\Component\DependencyInjection\ContainerBuilder;
18+
19+
/**
20+
* Propagates the transformers locator built by Symfony's JsonStreamer TransformerPass —
21+
* which merges "json_streamer.property_value_transformer" and "json_streamer.value_object_transformer"
22+
* services — onto API Platform's custom JSON-LD stream reader/writer.
23+
*
24+
* Without this, those custom services keep their initial tagged_locator argument and value
25+
* object transformers (DateTime, DateInterval, DateTimeZone) are missing from their locator,
26+
* which makes the JsonStreamer 8.1+ PhpGenerator fall back to a regular object provider that
27+
* calls Splitter::splitDict on date-time strings — "JSON is not valid".
28+
*
29+
* @internal
30+
*/
31+
final class JsonStreamerTransformerPass implements CompilerPassInterface
32+
{
33+
public function process(ContainerBuilder $container): void
34+
{
35+
if (!$container->hasDefinition('api_platform.jsonld.json_streamer.stream_reader')
36+
|| !$container->hasDefinition('json_streamer.stream_reader')) {
37+
return;
38+
}
39+
40+
$transformersArgument = $container->getDefinition('json_streamer.stream_reader')->getArgument(0);
41+
42+
$container->getDefinition('api_platform.jsonld.json_streamer.stream_reader')->replaceArgument(0, $transformersArgument);
43+
$container->getDefinition('api_platform.jsonld.json_streamer.stream_writer')->replaceArgument(0, $transformersArgument);
44+
}
45+
}

tests/Symfony/Bundle/ApiPlatformBundleTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\FilterPass;
2323
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\GraphQlResolverPass;
2424
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\GraphQlTypePass;
25+
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\JsonStreamerTransformerPass;
2526
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\MetadataAwareNameConverterPass;
2627
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\MutatorPass;
2728
use ApiPlatform\Symfony\Bundle\DependencyInjection\Compiler\SerializerMappingLoaderPass;
@@ -57,6 +58,7 @@ public function testBuild(): void
5758
$containerProphecy->addCompilerPass(Argument::type(AuthenticatorManagerPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
5859
$containerProphecy->addCompilerPass(Argument::type(SerializerMappingLoaderPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
5960
$containerProphecy->addCompilerPass(Argument::type(MutatorPass::class))->willReturn($containerProphecy->reveal())->shouldBeCalled();
61+
$containerProphecy->addCompilerPass(Argument::type(JsonStreamerTransformerPass::class), PassConfig::TYPE_BEFORE_OPTIMIZATION, -10)->willReturn($containerProphecy->reveal())->shouldBeCalled();
6062

6163
$bundle = new ApiPlatformBundle();
6264
$bundle->build($containerProphecy->reveal());

0 commit comments

Comments
 (0)