Skip to content

Commit a4c8d0a

Browse files
authored
Merge pull request #114 from tharropoulos/synonym-set-items
fix(synonyms/curation): fix nested item access for synonym sets and curation sets
2 parents a5da1d9 + 5361923 commit a4c8d0a

File tree

6 files changed

+305
-10
lines changed

6 files changed

+305
-10
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on: [push, pull_request]
44

55
jobs:
66
test:
7-
name: Run tests with PHP v8.3
7+
name: Run tests with PHP v8.4
88
runs-on: ubuntu-latest
99
steps:
1010
- name: Start Typesense
@@ -24,10 +24,10 @@ jobs:
2424
--enable-cors
2525
2626
- uses: actions/checkout@v4
27-
- name: Setup PHP 8.3
27+
- name: Setup PHP 8.4
2828
uses: shivammathur/setup-php@v2
2929
with:
30-
php-version: "8.3"
30+
php-version: "8.4"
3131
coverage: xdebug
3232
- uses: php-actions/composer@v6
3333
- name: Run tests

src/CurationSet.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ class CurationSet
2222
*/
2323
private ApiCall $apiCall;
2424

25-
/**
26-
* @var CurationSetItems
27-
*/
28-
private CurationSetItems $items;
25+
private array $typesenseCurationSetItems = [];
2926

3027
/**
3128
* CurationSet constructor.
@@ -37,7 +34,24 @@ public function __construct(string $curationSetName, ApiCall $apiCall)
3734
{
3835
$this->curationSetName = $curationSetName;
3936
$this->apiCall = $apiCall;
40-
$this->items = new CurationSetItems($curationSetName, $apiCall);
37+
}
38+
39+
/**
40+
* @param $id
41+
*
42+
* @return mixed
43+
*/
44+
public function __get($id)
45+
{
46+
if (isset($this->{$id})) {
47+
return $this->{$id};
48+
}
49+
50+
if (!isset($this->typesenseCurationSetItems[$id])) {
51+
$this->typesenseCurationSetItems[$id] = new CurationSetItems($this->curationSetName, $this->apiCall);
52+
}
53+
54+
return $this->typesenseCurationSetItems[$id];
4155
}
4256

4357
/**
@@ -86,6 +100,6 @@ public function delete(): array
86100
*/
87101
public function getItems(): CurationSetItems
88102
{
89-
return $this->items;
103+
return $this->__get('items');
90104
}
91105
}

src/SynonymSet.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class SynonymSet
2121
* @var ApiCall
2222
*/
2323
private ApiCall $apiCall;
24+
25+
private array $typesenseSynonymSetItems = [];
2426

2527
/**
2628
* SynonymSet constructor.
@@ -34,6 +36,24 @@ public function __construct(string $synonymSetName, ApiCall $apiCall)
3436
$this->apiCall = $apiCall;
3537
}
3638

39+
/**
40+
* @param $id
41+
*
42+
* @return mixed
43+
*/
44+
public function __get($id)
45+
{
46+
if (isset($this->{$id})) {
47+
return $this->{$id};
48+
}
49+
50+
if (!isset($this->typesenseSynonymSetItems[$id])) {
51+
$this->typesenseSynonymSetItems[$id] = new SynonymSetItems($this->synonymSetName, $this->apiCall);
52+
}
53+
54+
return $this->typesenseSynonymSetItems[$id];
55+
}
56+
3757
/**
3858
* @return string
3959
*/
@@ -74,4 +94,12 @@ public function delete(): array
7494
{
7595
return $this->apiCall->delete($this->endPointPath());
7696
}
77-
}
97+
98+
/**
99+
* @return SynonymSetItems
100+
*/
101+
public function getItems(): SynonymSetItems
102+
{
103+
return $this->__get('items');
104+
}
105+
}

src/SynonymSetItem.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
namespace Typesense;
4+
5+
use Http\Client\Exception as HttpClientException;
6+
use Typesense\Exceptions\TypesenseClientError;
7+
8+
/**
9+
* Class SynonymSetItem
10+
*
11+
* @package \Typesense
12+
*/
13+
class SynonymSetItem
14+
{
15+
/**
16+
* @var string
17+
*/
18+
private string $synonymSetName;
19+
20+
/**
21+
* @var string
22+
*/
23+
private string $itemId;
24+
25+
/**
26+
* @var ApiCall
27+
*/
28+
private ApiCall $apiCall;
29+
30+
/**
31+
* SynonymSetItem constructor.
32+
*
33+
* @param string $synonymSetName
34+
* @param string $itemId
35+
* @param ApiCall $apiCall
36+
*/
37+
public function __construct(string $synonymSetName, string $itemId, ApiCall $apiCall)
38+
{
39+
$this->synonymSetName = $synonymSetName;
40+
$this->itemId = $itemId;
41+
$this->apiCall = $apiCall;
42+
}
43+
44+
/**
45+
* @return string
46+
*/
47+
private function endPointPath(): string
48+
{
49+
return sprintf(
50+
'%s/%s/items/%s',
51+
SynonymSets::RESOURCE_PATH,
52+
encodeURIComponent($this->synonymSetName),
53+
encodeURIComponent($this->itemId)
54+
);
55+
}
56+
57+
/**
58+
* @return array
59+
* @throws TypesenseClientError|HttpClientException
60+
*/
61+
public function retrieve(): array
62+
{
63+
return $this->apiCall->get($this->endPointPath(), []);
64+
}
65+
66+
/**
67+
* @param array $params
68+
*
69+
* @return array
70+
* @throws TypesenseClientError|HttpClientException
71+
*/
72+
public function upsert(array $params): array
73+
{
74+
return $this->apiCall->put($this->endPointPath(), $params);
75+
}
76+
77+
/**
78+
* @return array
79+
* @throws TypesenseClientError|HttpClientException
80+
*/
81+
public function delete(): array
82+
{
83+
return $this->apiCall->delete($this->endPointPath());
84+
}
85+
}

src/SynonymSetItems.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
namespace Typesense;
4+
5+
use Http\Client\Exception as HttpClientException;
6+
use Typesense\Exceptions\TypesenseClientError;
7+
8+
/**
9+
* Class SynonymSetItems
10+
*
11+
* @package \Typesense
12+
*/
13+
class SynonymSetItems implements \ArrayAccess
14+
{
15+
/**
16+
* @var string
17+
*/
18+
private string $synonymSetName;
19+
20+
/**
21+
* @var ApiCall
22+
*/
23+
private ApiCall $apiCall;
24+
25+
/**
26+
* @var array
27+
*/
28+
private array $items = [];
29+
30+
/**
31+
* SynonymSetItems constructor.
32+
*
33+
* @param string $synonymSetName
34+
* @param ApiCall $apiCall
35+
*/
36+
public function __construct(string $synonymSetName, ApiCall $apiCall)
37+
{
38+
$this->synonymSetName = $synonymSetName;
39+
$this->apiCall = $apiCall;
40+
}
41+
42+
/**
43+
* @return string
44+
*/
45+
private function endPointPath(): string
46+
{
47+
return sprintf(
48+
'%s/%s/items',
49+
SynonymSets::RESOURCE_PATH,
50+
encodeURIComponent($this->synonymSetName)
51+
);
52+
}
53+
54+
/**
55+
* @return array
56+
* @throws TypesenseClientError|HttpClientException
57+
*/
58+
public function retrieve(): array
59+
{
60+
return $this->apiCall->get($this->endPointPath(), []);
61+
}
62+
63+
/**
64+
* @inheritDoc
65+
*/
66+
public function offsetExists($itemId): bool
67+
{
68+
return isset($this->items[$itemId]);
69+
}
70+
71+
/**
72+
* @inheritDoc
73+
*/
74+
public function offsetGet($itemId): SynonymSetItem
75+
{
76+
if (!isset($this->items[$itemId])) {
77+
$this->items[$itemId] = new SynonymSetItem($this->synonymSetName, $itemId, $this->apiCall);
78+
}
79+
80+
return $this->items[$itemId];
81+
}
82+
83+
/**
84+
* @inheritDoc
85+
*/
86+
public function offsetSet($itemId, $value): void
87+
{
88+
$this->items[$itemId] = $value;
89+
}
90+
91+
/**
92+
* @inheritDoc
93+
*/
94+
public function offsetUnset($itemId): void
95+
{
96+
unset($this->items[$itemId]);
97+
}
98+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Feature;
4+
5+
use Tests\TestCase;
6+
use Exception;
7+
8+
class SynonymSetItemsTest extends TestCase
9+
{
10+
private $synonymSets = null;
11+
private $synonymSetData = [
12+
'items' => [
13+
[
14+
'id' => 'synonym-rule-1',
15+
'synonyms' => ['foo', 'bar', 'baz'],
16+
'root' => '',
17+
],
18+
],
19+
];
20+
21+
protected function setUp(): void
22+
{
23+
parent::setUp();
24+
25+
if (!$this->isV30OrAbove()) {
26+
$this->markTestSkipped('SynonymSetItems is only supported in Typesense v30+');
27+
}
28+
29+
$this->synonymSets = $this->client()->synonymSets;
30+
$this->synonymSets->upsert('test-synonym-set-items', $this->synonymSetData);
31+
}
32+
33+
protected function tearDown(): void
34+
{
35+
try {
36+
if ($this->synonymSets !== null) {
37+
$this->synonymSets['test-synonym-set-items']->delete();
38+
}
39+
} catch (Exception $e) {
40+
// Ignore cleanup errors
41+
}
42+
parent::tearDown();
43+
}
44+
45+
public function testCanListItemsInASynonymSet(): void
46+
{
47+
$items = $this->synonymSets['test-synonym-set-items']->getItems()->retrieve();
48+
49+
$this->assertIsArray($items);
50+
$this->assertGreaterThan(0, count($items));
51+
$this->assertEquals('foo', $items[0]['synonyms'][0]);
52+
}
53+
54+
public function testCanUpsertRetrieveAndDeleteAnItem(): void
55+
{
56+
$upserted = $this->synonymSets['test-synonym-set-items']->getItems()['synonym-rule-1']->upsert([
57+
'id' => 'synonym-rule-1',
58+
'synonyms' => ['red', 'crimson'],
59+
'root' => '',
60+
]);
61+
62+
$this->assertEquals('synonym-rule-1', $upserted['id']);
63+
64+
$fetched = $this->synonymSets['test-synonym-set-items']->getItems()['synonym-rule-1']->retrieve();
65+
$this->assertEquals('red', $fetched['synonyms'][0]);
66+
67+
$deletion = $this->synonymSets['test-synonym-set-items']->getItems()['synonym-rule-1']->delete();
68+
$this->assertEquals('synonym-rule-1', $deletion['id']);
69+
}
70+
}

0 commit comments

Comments
 (0)