Skip to content

Commit 1a24f33

Browse files
authored
feat(php): add iterator helper methods (#936)
1 parent 41082f2 commit 1a24f33

File tree

7 files changed

+340
-1
lines changed

7 files changed

+340
-1
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
namespace Algolia\AlgoliaSearch\Iterators;
4+
5+
use Algolia\AlgoliaSearch\Api\SearchClient;
6+
7+
abstract class AbstractAlgoliaIterator implements \Iterator
8+
{
9+
protected $indexName;
10+
11+
/**
12+
* @var SearchClient
13+
*/
14+
protected $searchClient;
15+
16+
/**
17+
* @var array RequestOptions passed when getting new batch from Algolia
18+
*/
19+
protected $requestOptions;
20+
21+
/**
22+
* @var int
23+
*/
24+
protected $key = 0;
25+
26+
/**
27+
* @var int
28+
*/
29+
protected $batchKey = 0;
30+
31+
/**
32+
* @var int
33+
*/
34+
protected $page = 0;
35+
36+
/**
37+
* @var array response from the last Algolia API call,
38+
* this contains the results for the current page
39+
*/
40+
protected $response;
41+
42+
/**
43+
* Call Algolia' API to get new result batch.
44+
*/
45+
abstract protected function fetchNextPage();
46+
47+
/**
48+
* Sometimes the Iterator is using search internally, this method
49+
* is used to clean the results, like remove the highlight.
50+
*
51+
* @return array formatted synonym array
52+
*/
53+
abstract protected function formatHit(array $hit);
54+
55+
public function __construct(
56+
$indexName,
57+
SearchClient $searchClient,
58+
$requestOptions = []
59+
) {
60+
$this->indexName = $indexName;
61+
$this->searchClient = $searchClient;
62+
$this->requestOptions = $requestOptions + [
63+
'hitsPerPage' => 1000,
64+
];
65+
66+
$this->fetchNextPage();
67+
}
68+
69+
/**
70+
* Return the current element.
71+
*
72+
* @return array
73+
*/
74+
#[\ReturnTypeWillChange]
75+
public function current()
76+
{
77+
$hit = $this->response['hits'][$this->batchKey];
78+
79+
return $this->formatHit($hit);
80+
}
81+
82+
/**
83+
* Move forward to next element.
84+
*/
85+
#[\ReturnTypeWillChange]
86+
public function next()
87+
{
88+
$this->key++;
89+
$this->batchKey++;
90+
if ($this->valid()) {
91+
return;
92+
}
93+
94+
$this->fetchNextPage();
95+
}
96+
97+
/**
98+
* Return the key of the current element.
99+
*
100+
* @return int
101+
*/
102+
#[\ReturnTypeWillChange]
103+
public function key()
104+
{
105+
return $this->key;
106+
}
107+
108+
/**
109+
* Checks if current position is valid. If the current position
110+
* is not valid, we call Algolia' API to load more results
111+
* until it's the last page.
112+
*
113+
* @return bool the return value will be casted to boolean and then evaluated.
114+
* Returns true on success or false on failure
115+
*/
116+
#[\ReturnTypeWillChange]
117+
public function valid()
118+
{
119+
return isset($this->response['hits'][$this->batchKey]);
120+
}
121+
122+
/**
123+
* Rewind the Iterator to the first element.
124+
*/
125+
#[\ReturnTypeWillChange]
126+
public function rewind()
127+
{
128+
if (0 !== $this->key) {
129+
$this->key = 0;
130+
$this->batchKey = 0;
131+
$this->page = 0;
132+
$this->response = null;
133+
$this->fetchNextPage();
134+
}
135+
}
136+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Algolia\AlgoliaSearch\Iterators;
4+
5+
final class ObjectIterator extends AbstractAlgoliaIterator
6+
{
7+
public function getCursor()
8+
{
9+
return isset($this->response['cursor'])
10+
? $this->response['cursor']
11+
: null;
12+
}
13+
14+
/**
15+
* Exporting objects (records) doesn't use the search function but the
16+
* browse method, no client-side formatting is required.
17+
*
18+
* @return array the exact same $hit
19+
*/
20+
protected function formatHit(array $hit)
21+
{
22+
return $hit;
23+
}
24+
25+
protected function fetchNextPage()
26+
{
27+
if (is_array($this->response) && !isset($this->response['cursor'])) {
28+
return;
29+
}
30+
31+
$cursor = [];
32+
if (isset($this->response['cursor'])) {
33+
$cursor['cursor'] = $this->response['cursor'];
34+
}
35+
36+
$this->response = $this->searchClient->browse(
37+
$this->indexName,
38+
array_merge($this->requestOptions, $cursor)
39+
);
40+
41+
$this->batchKey = 0;
42+
}
43+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Algolia\AlgoliaSearch\Iterators;
4+
5+
final class RuleIterator extends AbstractAlgoliaIterator
6+
{
7+
protected function formatHit(array $hit)
8+
{
9+
unset($hit['_highlightResult']);
10+
11+
return $hit;
12+
}
13+
14+
protected function fetchNextPage()
15+
{
16+
if (
17+
is_array($this->response) &&
18+
$this->key >= $this->response['nbHits']
19+
) {
20+
return;
21+
}
22+
23+
$this->response = $this->searchClient->searchRules(
24+
$this->indexName,
25+
array_merge($this->requestOptions, ['page' => $this->page])
26+
);
27+
28+
$this->batchKey = 0;
29+
$this->page++;
30+
}
31+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Algolia\AlgoliaSearch\Iterators;
4+
5+
final class SynonymIterator extends AbstractAlgoliaIterator
6+
{
7+
protected function formatHit(array $hit)
8+
{
9+
unset($hit['_highlightResult']);
10+
11+
return $hit;
12+
}
13+
14+
protected function fetchNextPage()
15+
{
16+
if (
17+
is_array($this->response) &&
18+
$this->key >= $this->response['nbHits']
19+
) {
20+
return;
21+
}
22+
23+
$this->response = $this->searchClient->searchSynonyms(
24+
$this->indexName,
25+
null,
26+
$this->page,
27+
$this->requestOptions['hitsPerPage'],
28+
$this->requestOptions
29+
);
30+
31+
$this->batchKey = 0;
32+
$this->page++;
33+
}
34+
}

playground/php/src/search.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,30 @@
2727
],
2828
])
2929
);
30+
31+
// browse records
32+
$results = $client->browseObjects($indexName);
33+
34+
$objects = [];
35+
foreach ($results as $object) {
36+
$objects[] = $object;
37+
}
38+
var_dump($objects);
39+
40+
// browse synonyms
41+
$results = $client->browseSynonyms($indexName);
42+
43+
$synonyms = [];
44+
foreach ($results as $synonym) {
45+
$synonyms[] = $synonym;
46+
}
47+
var_dump($synonyms);
48+
49+
// browse rules
50+
$results = $client->browseRules($indexName);
51+
52+
$rules = [];
53+
foreach ($results as $rule) {
54+
$rules[] = $rule;
55+
}
56+
var_dump($rules);

templates/php/api.mustache

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use {{invokerPackage}}\Algolia;
77
use {{invokerPackage}}\ApiException;
88
use {{invokerPackage}}\Configuration\{{configClassname}};
99
use {{invokerPackage}}\Exceptions\ExceededRetriesException;
10+
use {{invokerPackage}}\Iterators\ObjectIterator;
11+
use {{invokerPackage}}\Iterators\RuleIterator;
12+
use {{invokerPackage}}\Iterators\SynonymIterator;
1013
use {{invokerPackage}}\ObjectSerializer;
1114
use {{invokerPackage}}\RetryStrategy\ApiWrapper;
1215
use {{invokerPackage}}\RetryStrategy\ApiWrapperInterface;
@@ -363,6 +366,45 @@ use {{invokerPackage}}\Support\Helpers;
363366
$requestOptions
364367
);
365368
}
369+
370+
/**
371+
* Helper: Iterate on the `browse` method of the client to allow aggregating objects of an index.
372+
*
373+
* @param string $indexName Index name
374+
* @param array $requestOptions Request options
375+
*
376+
* @return ObjectIterator
377+
*/
378+
public function browseObjects($indexName, $requestOptions = [])
379+
{
380+
return new ObjectIterator($indexName, $this, $requestOptions);
381+
}
382+
383+
/**
384+
* Helper: Iterate on the `searchRules` method of the client to allow aggregating rules of an index.
385+
*
386+
* @param string $indexName Index name
387+
* @param array $requestOptions Request options
388+
*
389+
* @return RuleIterator
390+
*/
391+
public function browseRules($indexName, $requestOptions = [])
392+
{
393+
return new RuleIterator($indexName, $this, $requestOptions);
394+
}
395+
396+
/**
397+
* Helper: Iterate on the `searchSynonyms` method of the client to allow aggregating synonyms of an index.
398+
*
399+
* @param string $indexName Index name
400+
* @param array $requestOptions Request options
401+
*
402+
* @return SynonymIterator
403+
*/
404+
public function browseSynonyms($indexName, $requestOptions = [])
405+
{
406+
return new SynonymIterator($indexName, $this, $requestOptions);
407+
}
366408
{{/isSearchClient}}
367409

368410
private function sendRequest($method, $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, $useReadTransporter = false)

website/docs/clients/migration-guides/index.mdx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,33 @@ console.log(synonyms, synonyms.length);
479479
<TabItem value="php">
480480

481481
```php
482-
// WIP
482+
483+
// browse records
484+
$results = $client->browseObjects($indexName);
485+
486+
$objects = [];
487+
foreach ($results as $object) {
488+
$objects[] = $object;
489+
}
490+
var_dump($objects);
491+
492+
// browse synonyms
493+
$results = $client->browseSynonyms($indexName);
494+
495+
$synonyms = [];
496+
foreach ($results as $synonym) {
497+
$synonyms[] = $synonym;
498+
}
499+
var_dump($synonyms);
500+
501+
// browse rules
502+
$results = $client->browseRules($indexName);
503+
504+
$rules = [];
505+
foreach ($results as $rule) {
506+
$rules[] = $rule;
507+
}
508+
var_dump($rules);
483509

484510
```
485511

0 commit comments

Comments
 (0)