Skip to content

Commit 22bdf3c

Browse files
committed
fix es
1 parent 0c2eafa commit 22bdf3c

3 files changed

Lines changed: 143 additions & 54 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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\HttpOperation;
17+
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
18+
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
19+
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
20+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
21+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
22+
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
23+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
24+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
25+
use Symfony\Component\Serializer\SerializerAwareInterface;
26+
use Symfony\Component\Serializer\SerializerInterface;
27+
28+
/**
29+
* Document denormalizer for Elasticsearch.
30+
*
31+
* @experimental
32+
*
33+
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
34+
*/
35+
final class DocumentDenormalizer implements DenormalizerInterface, SerializerAwareInterface
36+
{
37+
public const FORMAT = 'elasticsearch';
38+
39+
private readonly ObjectNormalizer $decoratedDenormalizer;
40+
41+
public function __construct(
42+
private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory,
43+
?ClassMetadataFactoryInterface $classMetadataFactory = null,
44+
private readonly ?NameConverterInterface $nameConverter = null,
45+
?PropertyAccessorInterface $propertyAccessor = null,
46+
?PropertyTypeExtractorInterface $propertyTypeExtractor = null,
47+
?ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null,
48+
?callable $objectClassResolver = null,
49+
array $defaultContext = [],
50+
) {
51+
$this->decoratedDenormalizer = new ObjectNormalizer($classMetadataFactory, $nameConverter, $propertyAccessor, $propertyTypeExtractor, $classDiscriminatorResolver, $objectClassResolver, $defaultContext);
52+
}
53+
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
58+
{
59+
return self::FORMAT === $format && $this->decoratedDenormalizer->supportsDenormalization($data, $type, $format, $context);
60+
}
61+
62+
/**
63+
* {@inheritdoc}
64+
*/
65+
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed
66+
{
67+
if (\is_string($data['_id'] ?? null) && \is_array($data['_source'] ?? null)) {
68+
$data = $this->populateIdentifier($data, $type)['_source'];
69+
}
70+
71+
return $this->decoratedDenormalizer->denormalize($data, $type, $format, $context);
72+
}
73+
74+
/**
75+
* Populates the resource identifier with the document identifier if not present in the original JSON document.
76+
*/
77+
private function populateIdentifier(array $data, string $class): array
78+
{
79+
$identifier = 'id';
80+
$resourceMetadata = $this->resourceMetadataCollectionFactory->create($class);
81+
82+
$operation = $resourceMetadata->getOperation();
83+
if ($operation instanceof HttpOperation) {
84+
$uriVariable = $operation->getUriVariables()[0] ?? null;
85+
86+
if ($uriVariable) {
87+
$identifier = $uriVariable->getIdentifiers()[0] ?? 'id';
88+
}
89+
}
90+
91+
$identifier = null === $this->nameConverter ? $identifier : $this->nameConverter->normalize($identifier, $class, self::FORMAT);
92+
93+
if (!isset($data['_source'][$identifier])) {
94+
$data['_source'][$identifier] = $data['_id'];
95+
}
96+
97+
return $data;
98+
}
99+
100+
/**
101+
* {@inheritdoc}
102+
*/
103+
public function setSerializer(SerializerInterface $serializer): void
104+
{
105+
$this->decoratedDenormalizer->setSerializer($serializer);
106+
}
107+
108+
/**
109+
* {@inheritdoc}
110+
*/
111+
public function getSupportedTypes(?string $format): array
112+
{
113+
return self::FORMAT === $format ? ['object' => true] : [];
114+
}
115+
}

src/Elasticsearch/Serializer/DocumentNormalizer.php

Lines changed: 14 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,30 @@
1313

1414
namespace ApiPlatform\Elasticsearch\Serializer;
1515

16-
use ApiPlatform\Metadata\HttpOperation;
17-
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
1816
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
1917
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
20-
use Symfony\Component\Serializer\Exception\LogicException;
2118
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
2219
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
2320
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
24-
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
2521
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
2622
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
2723
use Symfony\Component\Serializer\SerializerAwareInterface;
2824
use Symfony\Component\Serializer\SerializerInterface;
2925

3026
/**
31-
* Document denormalizer for Elasticsearch.
27+
* Document normalizer for Elasticsearch.
3228
*
3329
* @experimental
3430
*
3531
* @author Baptiste Meyer <baptiste.meyer@gmail.com>
3632
*/
37-
final class DocumentNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface
33+
final class DocumentNormalizer implements NormalizerInterface, SerializerAwareInterface
3834
{
3935
public const FORMAT = 'elasticsearch';
4036

4137
private readonly ObjectNormalizer $decoratedNormalizer;
4238

4339
public function __construct(
44-
private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory,
4540
?ClassMetadataFactoryInterface $classMetadataFactory = null,
4641
private readonly ?NameConverterInterface $nameConverter = null,
4742
?PropertyAccessorInterface $propertyAccessor = null,
@@ -56,66 +51,32 @@ public function __construct(
5651
/**
5752
* {@inheritdoc}
5853
*/
59-
public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
60-
{
61-
return self::FORMAT === $format && $this->decoratedNormalizer->supportsDenormalization($data, $type, $format, $context);
62-
}
63-
64-
/**
65-
* {@inheritdoc}
66-
*/
67-
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed
54+
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
6855
{
69-
if (\is_string($data['_id'] ?? null) && \is_array($data['_source'] ?? null)) {
70-
$data = $this->populateIdentifier($data, $type)['_source'];
56+
// Ensure that a resource is being normalized
57+
if (!\is_object($data)) {
58+
return false;
7159
}
7260

73-
return $this->decoratedNormalizer->denormalize($data, $type, $format, $context);
74-
}
75-
76-
/**
77-
* {@inheritdoc}
78-
*/
79-
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
80-
{
81-
// prevent the use of lower priority normalizers (e.g. serializer.normalizer.object) for this format
61+
// Only normalize for the elasticsearch format
8262
return self::FORMAT === $format;
8363
}
8464

8565
/**
8666
* {@inheritdoc}
87-
*
88-
* @throws LogicException
8967
*/
9068
public function normalize(mixed $data, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
9169
{
92-
throw new LogicException(\sprintf('%s is a write-only format.', self::FORMAT));
93-
}
94-
95-
/**
96-
* Populates the resource identifier with the document identifier if not present in the original JSON document.
97-
*/
98-
private function populateIdentifier(array $data, string $class): array
99-
{
100-
$identifier = 'id';
101-
$resourceMetadata = $this->resourceMetadataCollectionFactory->create($class);
102-
103-
$operation = $resourceMetadata->getOperation();
104-
if ($operation instanceof HttpOperation) {
105-
$uriVariable = $operation->getUriVariables()[0] ?? null;
106-
107-
if ($uriVariable) {
108-
$identifier = $uriVariable->getIdentifiers()[0] ?? 'id';
109-
}
110-
}
111-
112-
$identifier = null === $this->nameConverter ? $identifier : $this->nameConverter->normalize($identifier, $class, self::FORMAT);
70+
$normalizedData = $this->decoratedNormalizer->normalize($data, $format, $context);
11371

114-
if (!isset($data['_source'][$identifier])) {
115-
$data['_source'][$identifier] = $data['_id'];
72+
// Add _id and _source if not already present
73+
// This is a basic implementation and might need to be more sophisticated based on specific needs.
74+
// It assumes 'id' is the primary identifier for the resource.
75+
if (\is_array($normalizedData) && !isset($normalizedData['_id']) && isset($normalizedData['id'])) {
76+
$normalizedData = ['_id' => (string) $normalizedData['id'], '_source' => $normalizedData];
11677
}
11778

118-
return $data;
79+
return $normalizedData;
11980
}
12081

12182
/**

src/Symfony/Bundle/Resources/config/elasticsearch.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use ApiPlatform\Elasticsearch\Filter\OrderFilter;
2121
use ApiPlatform\Elasticsearch\Filter\TermFilter;
2222
use ApiPlatform\Elasticsearch\Metadata\Resource\Factory\ElasticsearchProviderResourceMetadataCollectionFactory;
23+
use ApiPlatform\Elasticsearch\Serializer\DocumentDenormalizer;
2324
use ApiPlatform\Elasticsearch\Serializer\DocumentNormalizer;
2425
use ApiPlatform\Elasticsearch\Serializer\ItemNormalizer;
2526
use ApiPlatform\Elasticsearch\Serializer\NameConverter\InnerFieldsNameConverter;
@@ -36,7 +37,7 @@
3637
->decorate('api_platform.serializer.normalizer.item', null, 0)
3738
->args([service('api_platform.elasticsearch.normalizer.item.inner')]);
3839

39-
$services->set('api_platform.elasticsearch.normalizer.document', DocumentNormalizer::class)
40+
$services->set('api_platform.elasticsearch.denormalizer.document', DocumentDenormalizer::class)
4041
->args([
4142
service('api_platform.metadata.resource.metadata_collection_factory'),
4243
service('serializer.mapping.class_metadata_factory'),
@@ -47,6 +48,18 @@
4748
null,
4849
'%api_platform.serializer.default_context%',
4950
])
51+
->tag('serializer.normalizer', ['priority' => -895]);
52+
53+
$services->set('api_platform.elasticsearch.normalizer.document', DocumentNormalizer::class)
54+
->args([
55+
service('serializer.mapping.class_metadata_factory'),
56+
service('api_platform.elasticsearch.name_converter.inner_fields'),
57+
service('serializer.property_accessor'),
58+
service('property_info')->ignoreOnInvalid(),
59+
service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(),
60+
null,
61+
'%api_platform.serializer.default_context%',
62+
])
5063
->tag('serializer.normalizer', ['priority' => -922]);
5164

5265
$services->set('api_platform.elasticsearch.request_body_search_extension.filter')

0 commit comments

Comments
 (0)