Skip to content

Commit bcb348f

Browse files
Allow to set Serializer Context for input/output! :) + some improvements
1 parent 4fb7eb4 commit bcb348f

File tree

5 files changed

+107
-18
lines changed

5 files changed

+107
-18
lines changed

README.md

+20-8
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ Opinionated [Elastica](https://github.com/ruflin/Elastica) based framework to bo
2121
Quick example of what the library do on top of Elastica:
2222

2323
```php
24-
<?php
25-
2624
// Your own DTO, or one generated by Jane (see below)
2725
class Beer
2826
{
@@ -100,6 +98,7 @@ $indexBuilder->purgeOldIndices('beers');
10098
```yaml
10199
# Anything you want, no validation
102100
mappings:
101+
dynamic: false
103102
properties:
104103
foo:
105104
type: text
@@ -113,15 +112,15 @@ mappings:
113112
114113
This library add custom configurations on top of Elastica's:
115114
116-
### Client::CONFIG_MAPPINGS_DIRECTORY
115+
### Client::CONFIG_MAPPINGS_DIRECTORY (required)
117116
118117
The directory Elastically is going to look for YAML.
119118
120119
When creating a `foobar` index, a `foobar.yaml` file is expected.
121120

122121
If an `analyzers.yaml` file is present, **all** the indices will get it.
123122

124-
### Client::CONFIG_INDEX_CLASS_MAPPING
123+
### Client::CONFIG_INDEX_CLASS_MAPPING (required)
125124

126125
An array of index name to class FQN.
127126

@@ -135,13 +134,27 @@ An array of index name to class FQN.
135134

136135
A `SerializerInterface` and `DenormalizerInterface` compatible object that will by used both on indexation and search.
137136

138-
Default to Symfony Object Normalizer which can be slow. See below for Jane usage.
137+
_Default to Symfony Object Normalizer._
138+
139+
A faster alternative is to use Jane to generate plain PHP Normalizer, see below. Also we recommend [customization to handle things like Date](https://symfony.com/doc/current/components/serializer.html#normalizers).
140+
141+
### Client::CONFIG_SERIALIZER_CONTEXT_PER_CLASS (optional)
139142

140-
*Todo: add custom demo?*
143+
Allow to specify the Serializer context for normalization and denormalization.
144+
145+
```php
146+
$client->setConfigValue(Client::CONFIG_SERIALIZER_CONTEXT_PER_CLASS, [
147+
Beer::class => ['attributes' => ['title']],
148+
]);
149+
```
150+
151+
_Default to `[]`._
141152

142153
### Client::CONFIG_BULK_SIZE (optional)
143154

144-
When running indexation of lots of documents, this setting allow you to fine-tune the number of document threshold. Default to 100.
155+
When running indexation of lots of documents, this setting allow you to fine-tune the number of document threshold.
156+
157+
_Default to 100._
145158

146159
## Using Jane for DTO and fast Normalizers
147160

@@ -166,7 +179,6 @@ $client = new Client([
166179
## To be done
167180

168181
- some "todo" in the code
169-
- serializer options (by index?!)
170182
- optional Doctrine connector
171183
- better logger
172184
- travis-ci setup

src/Client.php

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class Client extends ElasticaClient
1414
/* Elastically config keys */
1515
const CONFIG_MAPPINGS_DIRECTORY = 'elastically_mappings_directory';
1616
const CONFIG_INDEX_CLASS_MAPPING = 'elastically_index_class_mapping';
17+
const CONFIG_SERIALIZER_CONTEXT_PER_CLASS = 'elastically_serializer_context_per_class';
1718
const CONFIG_SERIALIZER = 'elastically_serializer';
1819
const CONFIG_BULK_SIZE = 'elastically_bulk_size';
1920

@@ -53,4 +54,11 @@ public function getSerializer(): SerializerInterface
5354
new JsonEncoder(),
5455
]);
5556
}
57+
58+
public function getSerializerContext($class): array
59+
{
60+
$configSerializer = $this->getConfigValue(self::CONFIG_SERIALIZER_CONTEXT_PER_CLASS);
61+
62+
return $configSerializer[$class] ?? [];
63+
}
5664
}

src/Indexer.php

+9-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace JoliCode\Elastically;
44

55
use Elastica\Bulk;
6-
use Elastica\Client;
76
use Elastica\Document;
87
use Elastica\Exception\Bulk\ResponseException;
98
use Elastica\Index;
@@ -29,8 +28,9 @@ public function __construct(Client $client, SerializerInterface $serializer, int
2928
public function scheduleIndex($index, Document $document)
3029
{
3130
$document->setIndex($index instanceof Index ? $index->getName() : $index);
32-
if (!is_string($document->getData())) {
33-
$document->setData($this->serializer->serialize($document->getData(), 'json'));
31+
if (is_object($document->getData())) {
32+
$context = $this->client->getSerializerContext(get_class($document->getData()));
33+
$document->setData($this->serializer->serialize($document->getData(), 'json', $context));
3434
}
3535

3636
$this->getCurrentBulk()->addDocument($document, Bulk\Action::OP_TYPE_INDEX);
@@ -54,8 +54,9 @@ public function scheduleDelete($index, $id)
5454
public function scheduleUpdate($index, Document $document)
5555
{
5656
$document->setIndex($index instanceof Index ? $index->getName() : $index);
57-
if (!is_string($document->getData())) {
58-
$document->setData($this->serializer->serialize($document->getData(), 'json'));
57+
if (is_object($document->getData())) {
58+
$context = $this->client->getSerializerContext(get_class($document->getData()));
59+
$document->setData($this->serializer->serialize($document->getData(), 'json', $context));
5960
}
6061

6162
$this->getCurrentBulk()->addDocument($document, Bulk\Action::OP_TYPE_UPDATE);
@@ -68,8 +69,9 @@ public function scheduleUpdate($index, Document $document)
6869
public function scheduleCreate($index, Document $document)
6970
{
7071
$document->setIndex($index instanceof Index ? $index->getName() : $index);
71-
if (!is_string($document->getData())) {
72-
$document->setData($this->serializer->serialize($document->getData(), 'json'));
72+
if (is_object($document->getData())) {
73+
$context = $this->client->getSerializerContext(get_class($document->getData()));
74+
$document->setData($this->serializer->serialize($document->getData(), 'json', $context));
7375
}
7476

7577
$this->getCurrentBulk()->addDocument($document, Bulk\Action::OP_TYPE_CREATE);

src/ResultSetBuilder.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ public function buildModelFromIndexAndData($indexName, $data)
5656
{
5757
$pureIndexName = IndexBuilder::getPureIndexName($indexName);
5858
$indexToClass = $this->client->getConfig(Client::CONFIG_INDEX_CLASS_MAPPING);
59-
6059
if (!isset($indexToClass[$pureIndexName])) {
6160
throw new RuntimeException(sprintf('Unknown class for index "%s", did you forgot to configure "%s"?', $pureIndexName, Client::CONFIG_INDEX_CLASS_MAPPING));
6261
}
6362

64-
return $this->client->getSerializer()->denormalize($data, $indexToClass[$pureIndexName]);
63+
$context = $this->client->getSerializerContext($indexToClass[$pureIndexName]);
64+
65+
return $this->client->getSerializer()->denormalize($data, $indexToClass[$pureIndexName], null, $context);
6566
}
6667
}

tests/IndexerTest.php

+67-1
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,38 @@ public function testIndexOneDocument(): void
3333
$indexer->refresh($indexName);
3434

3535
$client = $this->getClient();
36-
$document = $client->getIndex($indexName)->getDocument('f'); // @todo Document DATA is not the DTO here, call getModel?
36+
$document = $client->getIndex($indexName)->getDocument('f');
3737

3838
$this->assertInstanceOf(Document::class, $document);
3939
$this->assertEquals('f', $document->getId());
4040
}
4141

42+
public function testIndexOneDocumentWithMapping(): void
43+
{
44+
$indexName = mb_strtolower(__FUNCTION__);
45+
$client = $this->getClient();
46+
$client->setConfigValue(Client::CONFIG_INDEX_CLASS_MAPPING, [
47+
$indexName => TestDTO::class,
48+
]);
49+
50+
$dto = new TestDTO();
51+
$dto->bar = 'I like unicorns.';
52+
$dto->foo = 'Why is the sky blue?';
53+
54+
$indexer = $client->getIndexer();
55+
56+
$indexer->scheduleIndex($indexName, new Document('f', $dto));
57+
$indexer->flush();
58+
59+
$indexer->refresh($indexName);
60+
61+
$model = $client->getIndex($indexName)->getModel('f');
62+
63+
$this->assertInstanceOf(TestDTO::class, $model);
64+
$this->assertEquals($dto->bar, $model->bar);
65+
$this->assertEquals($dto->foo, $model->foo);
66+
}
67+
4268
public function testIndexMultipleDocuments(): void
4369
{
4470
$indexName = mb_strtolower(__FUNCTION__);
@@ -129,6 +155,46 @@ public function testIndexJsonString(): void
129155
$this->assertInstanceOf(ResponseSet::class, $response);
130156
$this->assertFalse($response->hasError());
131157
}
158+
159+
public function testCustomSerializerContext(): void
160+
{
161+
$indexName = mb_strtolower(__FUNCTION__);
162+
163+
$client = $this->getClient();
164+
$client->setConfigValue(Client::CONFIG_INDEX_CLASS_MAPPING, [
165+
$indexName => TestDTO::class,
166+
]);
167+
$client->setConfigValue(Client::CONFIG_SERIALIZER_CONTEXT_PER_CLASS, [
168+
TestDTO::class => ['attributes' => ['foo']],
169+
]);
170+
171+
$dto = new TestDTO();
172+
$dto->bar = 'I like unicorns.';
173+
$dto->foo = 'Why is the sky blue?';
174+
175+
$indexer = $client->getIndexer();
176+
$indexer->scheduleIndex($indexName, new Document('f', $dto));
177+
$indexer->flush();
178+
179+
$indexer->refresh($indexName);
180+
181+
$model = $client->getIndex($indexName)->getModel('f');
182+
183+
$this->assertInstanceOf(TestDTO::class, $model);
184+
$this->assertEquals($dto->foo, $model->foo);
185+
$this->assertEmpty($model->bar);
186+
187+
// Also work on read
188+
$client->setConfigValue(Client::CONFIG_SERIALIZER_CONTEXT_PER_CLASS, [
189+
TestDTO::class => ['attributes' => ['yolo']],
190+
]);
191+
192+
$model = $client->getIndex($indexName)->getModel('f');
193+
194+
$this->assertInstanceOf(TestDTO::class, $model);
195+
$this->assertEmpty($model->foo);
196+
$this->assertEmpty($model->bar);
197+
}
132198
}
133199

134200
class TestDTO

0 commit comments

Comments
 (0)