Skip to content

Commit 1c4c469

Browse files
committed
fix es
1 parent 22bdf3c commit 1c4c469

6 files changed

Lines changed: 300 additions & 186 deletions

File tree

src/Elasticsearch/Serializer/DocumentDenormalizer.php

Lines changed: 0 additions & 115 deletions
This file was deleted.

src/Elasticsearch/Serializer/DocumentNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ final class DocumentNormalizer implements NormalizerInterface, SerializerAwareIn
3838

3939
public function __construct(
4040
?ClassMetadataFactoryInterface $classMetadataFactory = null,
41-
private readonly ?NameConverterInterface $nameConverter = null,
41+
?NameConverterInterface $nameConverter = null,
4242
?PropertyAccessorInterface $propertyAccessor = null,
4343
?PropertyTypeExtractorInterface $propertyTypeExtractor = null,
4444
?ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null,
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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\Elasticsearch\Serializer;
15+
16+
use ApiPlatform\Metadata\ApiProperty;
17+
use ApiPlatform\Metadata\HttpOperation;
18+
use ApiPlatform\Metadata\IriConverterInterface;
19+
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
20+
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
21+
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
22+
use ApiPlatform\Metadata\ResourceAccessCheckerInterface;
23+
use ApiPlatform\Metadata\ResourceClassResolverInterface;
24+
use ApiPlatform\Serializer\AbstractItemNormalizer;
25+
use ApiPlatform\Serializer\TagCollectorInterface;
26+
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
27+
use Symfony\Component\Serializer\Exception\LogicException;
28+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
29+
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
30+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
31+
32+
/**
33+
* Item denormalizer for Elasticsearch.
34+
*
35+
* @experimental
36+
*
37+
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
38+
*/
39+
final class ItemDenormalizer extends AbstractItemNormalizer
40+
{
41+
public const FORMAT = 'elasticsearch';
42+
43+
public function __construct(
44+
PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory,
45+
PropertyMetadataFactoryInterface $propertyMetadataFactory,
46+
IriConverterInterface $iriConverter,
47+
ResourceClassResolverInterface $resourceClassResolver,
48+
?PropertyAccessorInterface $propertyAccessor = null,
49+
?NameConverterInterface $nameConverter = null,
50+
?ClassMetadataFactoryInterface $classMetadataFactory = null,
51+
array $defaultContext = [],
52+
?ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null,
53+
?ResourceAccessCheckerInterface $resourceAccessChecker = null,
54+
?TagCollectorInterface $tagCollector = null,
55+
) {
56+
parent::__construct(
57+
$propertyNameCollectionFactory,
58+
$propertyMetadataFactory,
59+
$iriConverter,
60+
$resourceClassResolver,
61+
$propertyAccessor,
62+
$nameConverter,
63+
$classMetadataFactory,
64+
$defaultContext,
65+
$resourceMetadataCollectionFactory,
66+
$resourceAccessChecker,
67+
$tagCollector
68+
);
69+
}
70+
71+
/**
72+
* {@inheritdoc}
73+
*/
74+
public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
75+
{
76+
return self::FORMAT === $format && parent::supportsDenormalization($data, $type, $format, $context);
77+
}
78+
79+
/**
80+
* {@inheritdoc}
81+
*/
82+
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed
83+
{
84+
// Handle Elasticsearch document structure with _id and _source
85+
if (\is_array($data) && \is_string($data['_id'] ?? null) && \is_array($data['_source'] ?? null)) {
86+
$data = $this->populateIdentifier($data, $type)['_source'];
87+
}
88+
89+
return parent::denormalize($data, $type, $format, $context);
90+
}
91+
92+
/**
93+
* Populates the resource identifier with the document identifier if not present in the original JSON document.
94+
*/
95+
private function populateIdentifier(array $data, string $class): array
96+
{
97+
$identifier = 'id';
98+
$resourceMetadata = $this->resourceMetadataCollectionFactory->create($class);
99+
100+
$operation = $resourceMetadata->getOperation();
101+
if ($operation instanceof HttpOperation) {
102+
$uriVariable = $operation->getUriVariables()[0] ?? null;
103+
104+
if ($uriVariable) {
105+
$identifier = $uriVariable->getIdentifiers()[0] ?? 'id';
106+
}
107+
}
108+
109+
$identifier = null === $this->nameConverter ? $identifier : $this->nameConverter->normalize($identifier, $class, self::FORMAT);
110+
111+
if (!isset($data['_source'][$identifier])) {
112+
$data['_source'][$identifier] = $data['_id'];
113+
}
114+
115+
return $data;
116+
}
117+
118+
/**
119+
* {@inheritdoc}
120+
*/
121+
public function getSupportedTypes(?string $format): array
122+
{
123+
return self::FORMAT === $format ? ['object' => true] : [];
124+
}
125+
126+
/**
127+
* {@inheritdoc}
128+
*
129+
* For Elasticsearch, we always allow nested documents because that's how ES stores and returns data.
130+
*/
131+
protected function denormalizeRelation(string $attributeName, ApiProperty $propertyMetadata, string $className, mixed $value, ?string $format, array $context): ?object
132+
{
133+
// For Elasticsearch, always allow nested documents
134+
$context['api_allow_update'] = true;
135+
136+
if (!$this->serializer instanceof DenormalizerInterface) {
137+
throw new LogicException(\sprintf('The injected serializer must be an instance of "%s".', DenormalizerInterface::class));
138+
}
139+
140+
$item = $this->serializer->denormalize($value, $className, $format, $context);
141+
if (!\is_object($item) && null !== $item) {
142+
throw new \UnexpectedValueException('Expected item to be an object or null.');
143+
}
144+
145+
return $item;
146+
}
147+
}

0 commit comments

Comments
 (0)