Skip to content

Commit 2d73e42

Browse files
Merge #321
321: Implement Doctrine discriminator maps for dynamic entity management r=norkunas a=james2001 # Pull Request ## What does this PR do? - Implements Doctrine discriminator maps to enable dynamic entity management. This enhancement addresses the issue where indexing abstract or extended entities was not possible, by allowing efficient management of different entities within the same inheritance hierarchy. This simplifies data model extension and maintenance, making the system more flexible and robust. ## PR checklist Please check if your PR fulfills the following requirements: - [x] Fixes the issue with indexing abstract or extended entities by utilizing Doctrine's discriminator maps, representing a significant architectural improvement. - [x] I have read the contributing guidelines. - [x] The title of the PR is accurate and descriptive of the changes made. Co-authored-by: Stephane Rathgeber <[email protected]>
2 parents b61709a + 0d4e71b commit 2d73e42

File tree

7 files changed

+183
-5
lines changed

7 files changed

+183
-5
lines changed

src/Services/MeilisearchService.php

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@ public function __construct(NormalizerInterface $normalizer, Engine $engine, arr
5757

5858
public function isSearchable($className): bool
5959
{
60-
if (is_object($className)) {
61-
$className = ClassUtils::getClass($className);
62-
}
60+
$className = $this->getBaseClassName($className);
6361

6462
return in_array($className, $this->searchableEntities, true);
6563
}
@@ -76,6 +74,8 @@ public function getConfiguration(): Collection
7674

7775
public function searchableAs(string $className): string
7876
{
77+
$className = $this->getBaseClassName($className);
78+
7979
$indexes = new Collection($this->getConfiguration()->get('indices'));
8080
$index = $indexes->firstWhere('class', $className);
8181

@@ -207,7 +207,8 @@ public function count(string $className, string $query = '', array $searchParams
207207

208208
public function shouldBeIndexed(object $entity): bool
209209
{
210-
$className = ClassUtils::getClass($entity);
210+
$className = $this->getBaseClassName($entity);
211+
211212
$propertyPath = $this->indexIfMapping[$className];
212213

213214
if (null !== $propertyPath) {
@@ -221,6 +222,26 @@ public function shouldBeIndexed(object $entity): bool
221222
return true;
222223
}
223224

225+
/**
226+
* @param object|class-string $objectOrClass
227+
*
228+
* @return class-string
229+
*/
230+
private function getBaseClassName($objectOrClass): string
231+
{
232+
foreach ($this->searchableEntities as $class) {
233+
if (is_a($objectOrClass, $class, true)) {
234+
return $class;
235+
}
236+
}
237+
238+
if (is_object($objectOrClass)) {
239+
return ClassUtils::getClass($objectOrClass);
240+
}
241+
242+
return $objectOrClass;
243+
}
244+
224245
private function setSearchableEntities(): void
225246
{
226247
$searchable = [];
@@ -313,7 +334,7 @@ private function makeSearchServiceResponseFrom(
313334
foreach (array_chunk($entities, $this->configuration->get('batchSize')) as $chunk) {
314335
$searchableEntitiesChunk = [];
315336
foreach ($chunk as $entity) {
316-
$entityClassName = ClassUtils::getClass($entity);
337+
$entityClassName = $this->getBaseClassName($entity);
317338

318339
$searchableEntitiesChunk[] = new SearchableEntity(
319340
$this->searchableAs($entityClassName),

tests/BaseKernelTestCase.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
use Meilisearch\Bundle\Collection;
1010
use Meilisearch\Bundle\SearchableEntity;
1111
use Meilisearch\Bundle\SearchService;
12+
use Meilisearch\Bundle\Tests\Entity\Article;
1213
use Meilisearch\Bundle\Tests\Entity\Comment;
1314
use Meilisearch\Bundle\Tests\Entity\Image;
1415
use Meilisearch\Bundle\Tests\Entity\Link;
1516
use Meilisearch\Bundle\Tests\Entity\ObjectId\DummyObjectId;
1617
use Meilisearch\Bundle\Tests\Entity\Page;
18+
use Meilisearch\Bundle\Tests\Entity\Podcast;
1719
use Meilisearch\Bundle\Tests\Entity\Post;
1820
use Meilisearch\Bundle\Tests\Entity\Tag;
1921
use Meilisearch\Client;
@@ -116,6 +118,34 @@ protected function createImage(?int $id = null): Image
116118
return $image;
117119
}
118120

121+
protected function createArticle(?int $id = null): Article
122+
{
123+
$article = new Article();
124+
$article->setTitle('Test Article');
125+
if (null !== $id) {
126+
$article->setId($id);
127+
}
128+
129+
$this->entityManager->persist($article);
130+
$this->entityManager->flush();
131+
132+
return $article;
133+
}
134+
135+
protected function createPodcast(?int $id = null): Podcast
136+
{
137+
$podcast = new Podcast();
138+
$podcast->setTitle('Test Podcast');
139+
if (null !== $id) {
140+
$podcast->setId($id);
141+
}
142+
143+
$this->entityManager->persist($podcast);
144+
$this->entityManager->flush();
145+
146+
return $podcast;
147+
}
148+
119149
protected function createSearchableImage(): SearchableEntity
120150
{
121151
$image = $this->createImage(random_int(100, 300));

tests/Entity/Article.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Meilisearch\Bundle\Tests\Entity;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
9+
/**
10+
* @ORM\Entity
11+
*/
12+
#[ORM\Entity]
13+
class Article extends ContentItem
14+
{
15+
}

tests/Entity/ContentItem.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Meilisearch\Bundle\Tests\Entity;
6+
7+
use Doctrine\DBAL\Types\Types;
8+
use Doctrine\ORM\Mapping as ORM;
9+
10+
/**
11+
* @ORM\Entity
12+
*
13+
* @ORM\InheritanceType("JOINED")
14+
*
15+
* @ORM\DiscriminatorColumn(name="type", type="integer")
16+
*
17+
* @ORM\DiscriminatorMap({1 = Article::class, 2 = Podcast::class})
18+
*/
19+
#[ORM\Entity]
20+
#[ORM\InheritanceType('JOINED')]
21+
#[ORM\DiscriminatorColumn(name: 'type', type: 'integer')]
22+
#[ORM\DiscriminatorMap([1 => Article::class, 2 => Podcast::class])]
23+
abstract class ContentItem
24+
{
25+
/**
26+
* @ORM\Id
27+
*
28+
* @ORM\GeneratedValue
29+
*
30+
* @ORM\Column(type="integer")
31+
*/
32+
#[ORM\Id]
33+
#[ORM\GeneratedValue]
34+
#[ORM\Column(type: Types::INTEGER)]
35+
private int $id;
36+
37+
/**
38+
* @ORM\Column(type="string")
39+
*/
40+
#[ORM\Column(type: Types::STRING)]
41+
private string $title = 'Title';
42+
43+
public function getId(): int
44+
{
45+
return $this->id;
46+
}
47+
48+
public function setId(int $id): self
49+
{
50+
$this->id = $id;
51+
52+
return $this;
53+
}
54+
55+
public function getTitle(): string
56+
{
57+
return $this->title;
58+
}
59+
60+
public function setTitle(string $title): self
61+
{
62+
$this->title = $title;
63+
64+
return $this;
65+
}
66+
}

tests/Entity/Podcast.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Meilisearch\Bundle\Tests\Entity;
6+
7+
use Doctrine\ORM\Mapping as ORM;
8+
9+
/**
10+
* @ORM\Entity
11+
*/
12+
#[ORM\Entity]
13+
class Podcast extends ContentItem
14+
{
15+
}

tests/Integration/CommandsTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public function testSearchImportAndClearAndDeleteWithoutIndices(): void
8080
Indexed a batch of 6 / 6 Meilisearch\Bundle\Tests\Entity\Tag entities into sf_phpunit__tags index (6 indexed since start)
8181
Indexed a batch of 6 / 6 Meilisearch\Bundle\Tests\Entity\Tag entities into sf_phpunit__aggregated index (6 indexed since start)
8282
Importing for index Meilisearch\Bundle\Tests\Entity\Link
83+
Importing for index Meilisearch\Bundle\Tests\Entity\ContentItem
8384
Importing for index Meilisearch\Bundle\Tests\Entity\Page
8485
Indexed a batch of 6 / 6 Meilisearch\Bundle\Tests\Entity\Page entities into sf_phpunit__pages index (6 indexed since start)
8586
Importing for index Meilisearch\Bundle\Tests\Entity\SelfNormalizable
@@ -111,6 +112,7 @@ public function testSearchImportAndClearAndDeleteWithoutIndices(): void
111112
Cleared sf_phpunit__aggregated index of Meilisearch\Bundle\Tests\Entity\ContentAggregator
112113
Cleared sf_phpunit__tags index of Meilisearch\Bundle\Tests\Entity\Tag
113114
Cleared sf_phpunit__tags index of Meilisearch\Bundle\Tests\Entity\Link
115+
Cleared sf_phpunit__discriminator_map index of Meilisearch\Bundle\Tests\Entity\ContentItem
114116
Cleared sf_phpunit__pages index of Meilisearch\Bundle\Tests\Entity\Page
115117
Cleared sf_phpunit__self_normalizable index of Meilisearch\Bundle\Tests\Entity\SelfNormalizable
116118
Cleared sf_phpunit__dummy_custom_groups index of Meilisearch\Bundle\Tests\Entity\DummyCustomGroups
@@ -130,6 +132,7 @@ public function testSearchImportAndClearAndDeleteWithoutIndices(): void
130132
Deleted sf_phpunit__comments
131133
Deleted sf_phpunit__aggregated
132134
Deleted sf_phpunit__tags
135+
Deleted sf_phpunit__discriminator_map
133136
Deleted sf_phpunit__pages
134137
Deleted sf_phpunit__self_normalizable
135138
Deleted sf_phpunit__dummy_custom_groups
@@ -157,6 +160,30 @@ public function testImportWithoutUpdatingSettings(): void
157160
Indexed a batch of 6 / 6 Meilisearch\Bundle\Tests\Entity\Post entities into sf_phpunit__aggregated index (6 indexed since start)
158161
Done!
159162

163+
EOD, $importOutput);
164+
}
165+
166+
public function testImportContentItem(): void
167+
{
168+
for ($i = 0; $i <= 5; ++$i) {
169+
$this->createArticle();
170+
}
171+
172+
for ($i = 0; $i <= 5; ++$i) {
173+
$this->createPodcast();
174+
}
175+
176+
$importCommand = $this->application->find('meilisearch:import');
177+
$importCommandTester = new CommandTester($importCommand);
178+
$importCommandTester->execute(['--indices' => 'discriminator_map', '--no-update-settings' => true]);
179+
180+
$importOutput = $importCommandTester->getDisplay();
181+
182+
$this->assertSame(<<<'EOD'
183+
Importing for index Meilisearch\Bundle\Tests\Entity\ContentItem
184+
Indexed a batch of 12 / 12 Meilisearch\Bundle\Tests\Entity\ContentItem entities into sf_phpunit__discriminator_map index (12 indexed since start)
185+
Done!
186+
160187
EOD, $importOutput);
161188
}
162189

@@ -359,6 +386,7 @@ public function testSearchCreateWithoutIndices(bool $updateSettings): void
359386
Creating index sf_phpunit__comments for Meilisearch\Bundle\Tests\Entity\Comment
360387
Creating index sf_phpunit__tags for Meilisearch\Bundle\Tests\Entity\Tag
361388
Creating index sf_phpunit__tags for Meilisearch\Bundle\Tests\Entity\Link
389+
Creating index sf_phpunit__discriminator_map for Meilisearch\Bundle\Tests\Entity\ContentItem
362390
Creating index sf_phpunit__pages for Meilisearch\Bundle\Tests\Entity\Page
363391
Creating index sf_phpunit__self_normalizable for Meilisearch\Bundle\Tests\Entity\SelfNormalizable
364392
Creating index sf_phpunit__dummy_custom_groups for Meilisearch\Bundle\Tests\Entity\DummyCustomGroups
@@ -378,6 +406,7 @@ public function testSearchCreateWithoutIndices(bool $updateSettings): void
378406
Creating index sf_phpunit__comments for Meilisearch\Bundle\Tests\Entity\Comment
379407
Creating index sf_phpunit__tags for Meilisearch\Bundle\Tests\Entity\Tag
380408
Creating index sf_phpunit__tags for Meilisearch\Bundle\Tests\Entity\Link
409+
Creating index sf_phpunit__discriminator_map for Meilisearch\Bundle\Tests\Entity\ContentItem
381410
Creating index sf_phpunit__pages for Meilisearch\Bundle\Tests\Entity\Page
382411
Creating index sf_phpunit__self_normalizable for Meilisearch\Bundle\Tests\Entity\SelfNormalizable
383412
Creating index sf_phpunit__dummy_custom_groups for Meilisearch\Bundle\Tests\Entity\DummyCustomGroups

tests/config/meilisearch.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ meilisearch:
3131
- name: tags
3232
class: 'Meilisearch\Bundle\Tests\Entity\Link'
3333
index_if: isSponsored
34+
- name: discriminator_map
35+
class: 'Meilisearch\Bundle\Tests\Entity\ContentItem'
3436
- name: pages
3537
class: 'Meilisearch\Bundle\Tests\Entity\Page'
3638
enable_serializer_groups: true

0 commit comments

Comments
 (0)