Skip to content

Commit 9328686

Browse files
committed
Wrap search response in SearchResults object with array access/iterator
1 parent c5bf539 commit 9328686

18 files changed

+383
-31
lines changed

.php-cs-fixer.dist.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@
2222
'import_constants' => false,
2323
],
2424
'no_superfluous_phpdoc_tags' => false,
25-
// @todo: when we'll support only PHP 8.0 and upper, we can enable `parameters` for `trailing_comma_in_multiline` rule
26-
'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['array_destructuring', 'arrays', 'match'/* , 'parameters' */]],
27-
// @todo: when we'll support only PHP 8.0 and upper, we can enable this
28-
'get_class_to_class_keyword' => false,
25+
'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['array_destructuring', 'arrays', 'match', 'parameters']],
26+
'get_class_to_class_keyword' => true,
27+
'phpdoc_to_comment' => ['ignored_tags' => ['var']], // changes phpdoc and breaks SCA
2928
]);

src/DataProvider/OrmEntityProvider.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ public function loadByIdentifiers(array $identifiers): array
7171

7272
public function getIdentifierValues(object $object): array
7373
{
74-
$manager = $this->managerRegistry->getManagerForClass(\get_class($object));
74+
$manager = $this->managerRegistry->getManagerForClass($object::class);
7575

76-
return $manager->getClassMetadata(\get_class($object))->getIdentifierValues($object);
76+
return $manager->getClassMetadata($object::class)->getIdentifierValues($object);
7777
}
7878

7979
public function normalizeIdentifiers(array $identifiers): string|int

src/Model/SearchResults.php

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Meilisearch\Bundle\Model;
6+
7+
/**
8+
* @template T of object
9+
*/
10+
final class SearchResults implements \ArrayAccess, \Countable, \IteratorAggregate, \JsonSerializable
11+
{
12+
/**
13+
* @var array<T>
14+
*/
15+
private readonly array $hits;
16+
private readonly int $hitsCount;
17+
private readonly string $query;
18+
private readonly int $processingTimeMs;
19+
20+
private readonly ?int $limit;
21+
private readonly ?int $offset;
22+
private readonly ?int $estimatedTotalHits;
23+
private readonly ?int $nbHits;
24+
25+
private readonly ?int $page;
26+
private readonly ?int $hitsPerPage;
27+
private readonly ?int $totalHits;
28+
private readonly ?int $totalPages;
29+
30+
private readonly int $semanticHitCount;
31+
32+
/**
33+
* @var array<string, mixed>
34+
*/
35+
private readonly array $facetDistribution;
36+
37+
/**
38+
* @var array<string, mixed>
39+
*/
40+
private readonly array $facetStats;
41+
42+
private readonly ?string $requestUid;
43+
44+
private readonly array $raw;
45+
46+
/**
47+
* @param array<T> $hits
48+
* @param array<string, mixed> $facetDistribution
49+
* @param array<string, mixed> $facetStats
50+
*/
51+
public function __construct(
52+
array $hits,
53+
string $query,
54+
int $processingTimeMs,
55+
56+
?int $limit = null,
57+
?int $offset = null,
58+
?int $estimatedTotalHits = null,
59+
?int $nbHits = null,
60+
61+
?int $page = null,
62+
?int $hitsPerPage = null,
63+
?int $totalHits = null,
64+
?int $totalPages = null,
65+
66+
int $semanticHitCount = 0,
67+
68+
array $facetDistribution = [],
69+
array $facetStats = [],
70+
71+
?string $requestUid = null,
72+
73+
array $raw = [],
74+
) {
75+
$this->hits = $hits;
76+
$this->hitsCount = \count($hits);
77+
$this->query = $query;
78+
$this->processingTimeMs = $processingTimeMs;
79+
80+
$this->limit = $limit;
81+
$this->offset = $offset;
82+
$this->estimatedTotalHits = $estimatedTotalHits;
83+
$this->nbHits = $nbHits;
84+
85+
$this->page = $page;
86+
$this->hitsPerPage = $hitsPerPage;
87+
$this->totalHits = $totalHits;
88+
$this->totalPages = $totalPages;
89+
90+
$this->semanticHitCount = $semanticHitCount;
91+
92+
$this->facetDistribution = $facetDistribution;
93+
$this->facetStats = $facetStats;
94+
95+
$this->requestUid = $requestUid;
96+
97+
$this->raw = $raw;
98+
}
99+
100+
/**
101+
* @return array<T>
102+
*/
103+
public function getHits(): array
104+
{
105+
return $this->hits;
106+
}
107+
108+
public function getHitsCount(): int
109+
{
110+
return $this->hitsCount;
111+
}
112+
113+
public function getQuery(): string
114+
{
115+
return $this->query;
116+
}
117+
118+
public function getProcessingTimeMs(): int
119+
{
120+
return $this->processingTimeMs;
121+
}
122+
123+
public function getLimit(): ?int
124+
{
125+
return $this->limit;
126+
}
127+
128+
public function getOffset(): ?int
129+
{
130+
return $this->offset;
131+
}
132+
133+
public function getEstimatedTotalHits(): ?int
134+
{
135+
return $this->estimatedTotalHits;
136+
}
137+
138+
public function getNbHits(): ?int
139+
{
140+
return $this->nbHits;
141+
}
142+
143+
public function getPage(): ?int
144+
{
145+
return $this->page;
146+
}
147+
148+
public function getHitsPerPage(): ?int
149+
{
150+
return $this->hitsPerPage;
151+
}
152+
153+
public function getTotalHits(): ?int
154+
{
155+
return $this->totalHits;
156+
}
157+
158+
public function getTotalPages(): ?int
159+
{
160+
return $this->totalPages;
161+
}
162+
163+
public function getSemanticHitCount(): int
164+
{
165+
return $this->semanticHitCount;
166+
}
167+
168+
/**
169+
* @return array<string, mixed>
170+
*/
171+
public function getFacetDistribution(): array
172+
{
173+
return $this->facetDistribution;
174+
}
175+
176+
/**
177+
* @return array<string, mixed>
178+
*/
179+
public function getFacetStats(): array
180+
{
181+
return $this->facetStats;
182+
}
183+
184+
public function getRequestUid(): ?string
185+
{
186+
return $this->requestUid;
187+
}
188+
189+
/**
190+
* @return array<string, mixed>
191+
*/
192+
public function getRaw(): array
193+
{
194+
return $this->raw;
195+
}
196+
197+
public function offsetExists(mixed $offset): bool
198+
{
199+
return isset($this->hits[$offset]);
200+
}
201+
202+
/**
203+
* @return T
204+
*/
205+
public function offsetGet(mixed $offset): mixed
206+
{
207+
return $this->hits[$offset];
208+
}
209+
210+
public function offsetSet(mixed $offset, mixed $value): void
211+
{
212+
throw new \LogicException('Cannot modify hits');
213+
}
214+
215+
public function offsetUnset(mixed $offset): void
216+
{
217+
throw new \LogicException('Cannot modify hits');
218+
}
219+
220+
public function count(): int
221+
{
222+
return \count($this->hits);
223+
}
224+
225+
public function getIterator(): \Traversable
226+
{
227+
return new \ArrayIterator($this->hits);
228+
}
229+
230+
public function jsonSerialize(): array
231+
{
232+
return [
233+
'hits' => $this->hits,
234+
'query' => $this->query,
235+
'processingTimeMs' => $this->processingTimeMs,
236+
'limit' => $this->limit,
237+
'offset' => $this->offset,
238+
'estimatedTotalHits' => $this->estimatedTotalHits,
239+
'nbHits' => $this->nbHits,
240+
'page' => $this->page,
241+
'hitsPerPage' => $this->hitsPerPage,
242+
'totalHits' => $this->totalHits,
243+
'totalPages' => $this->totalPages,
244+
'semanticHitCount' => $this->semanticHitCount,
245+
'facetDistribution' => $this->facetDistribution,
246+
'facetStats' => $this->facetStats,
247+
'requestUid' => $this->requestUid,
248+
];
249+
}
250+
}

src/SearchManagerInterface.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Meilisearch\Bundle;
66

77
use Meilisearch\Bundle\Exception\NotSearchableException;
8+
use Meilisearch\Bundle\Model\SearchResults;
89

910
/**
1011
* @phpstan-import-type IndexDeletionTask from Engine
@@ -81,11 +82,11 @@ public function delete(string $className): array;
8182
* @param class-string<T> $className
8283
* @param array<mixed> $searchParams
8384
*
84-
* @return list<T>
85+
* @return SearchResults<T>
8586
*
8687
* @throws NotSearchableException
8788
*/
88-
public function search(string $className, string $query = '', array $searchParams = []): array;
89+
public function search(string $className, string $query = '', array $searchParams = []): SearchResults;
8990

9091
/**
9192
* @param class-string $className

src/SearchService.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public function search(
6060
ObjectManager $objectManager,
6161
string $className,
6262
string $query = '',
63-
array $searchParams = []
63+
array $searchParams = [],
6464
): array;
6565

6666
/**
@@ -73,7 +73,7 @@ public function search(
7373
public function rawSearch(
7474
string $className,
7575
string $query = '',
76-
array $searchParams = []
76+
array $searchParams = [],
7777
): array;
7878

7979
/**

src/SearchableEntity.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function __construct(
4444
$entity,
4545
ClassMetadata $entityMetadata,
4646
?NormalizerInterface $normalizer = null,
47-
array $extra = []
47+
array $extra = [],
4848
) {
4949
$this->indexUid = $indexUid;
5050
$this->entity = $entity;

src/SearchableObject.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function __construct(
3131
private readonly object $object,
3232
private readonly \Stringable|string|int $identifier,
3333
private readonly NormalizerInterface $normalizer,
34-
array $normalizationContext = []
34+
array $normalizationContext = [],
3535
) {
3636
$this->normalizationContext = array_merge($normalizationContext, ['meilisearch' => true]);
3737
}

src/Services/MeilisearchManager.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Meilisearch\Bundle\Engine;
1414
use Meilisearch\Bundle\Exception\NotSearchableException;
1515
use Meilisearch\Bundle\Model\Aggregator;
16+
use Meilisearch\Bundle\Model\SearchResults;
1617
use Meilisearch\Bundle\SearchableObject;
1718
use Meilisearch\Bundle\SearchManagerInterface;
1819
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -192,18 +193,27 @@ public function delete(string $className): array
192193
return $this->engine->delete($this->searchableAs($className));
193194
}
194195

196+
/**
197+
* @template T of object
198+
*
199+
* @param class-string<T> $className
200+
*
201+
* @return SearchResults<T>
202+
*/
195203
public function search(
196204
string $className,
197205
string $query = '',
198206
array $searchParams = [],
199-
): array {
207+
): SearchResults {
200208
$this->assertIsSearchable($className);
201209

202210
$response = $this->engine->search($query, $this->searchableAs($className), $searchParams + ['limit' => $this->configuration->get('nbResults')]);
211+
$response['raw'] = $response;
203212
$hits = $response[self::RESULT_KEY_HITS];
204213

205214
if ([] === $hits) {
206-
return [];
215+
/** @var SearchResults<T> */
216+
return new SearchResults(...$response);
207217
}
208218

209219
$baseClassName = $this->getBaseClassName($className);
@@ -232,13 +242,16 @@ public function search(
232242
}
233243
}
234244

235-
return $results;
245+
$response['hits'] = $results;
246+
247+
/** @var SearchResults<T> */
248+
return new SearchResults(...$response);
236249
}
237250

238251
public function rawSearch(
239252
string $className,
240253
string $query = '',
241-
array $searchParams = []
254+
array $searchParams = [],
242255
): array {
243256
$this->assertIsSearchable($className);
244257

@@ -405,7 +418,7 @@ private static function resolveClass(object $object): string
405418
$resolver ??= (static function () {
406419
// Native lazy objects compatibility
407420
if (PHP_VERSION_ID >= 80400 && class_exists(LegacyReflectionFields::class)) {
408-
return fn (object $object) => \get_class($object);
421+
return fn (object $object) => $object::class;
409422
}
410423

411424
// Doctrine ORM v3+ compatibility

0 commit comments

Comments
 (0)