Skip to content

Commit a4807fc

Browse files
committed
feat: add support for virtual pages, pulling content from the original source.
1 parent 8d32123 commit a4807fc

File tree

4 files changed

+188
-71
lines changed

4 files changed

+188
-71
lines changed

_config/config.yml

+6
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ Name: silverstripe-algolia
44
SilverStripe\CMS\Model\SiteTree:
55
extensions:
66
- Wilr\SilverStripe\Algolia\Extensions\AlgoliaObjectExtension
7+
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
8+
extensions:
9+
- Wilr\SilverStripe\Algolia\Extensions\SubsitesVirtualPageExtension
10+
ilverStripe\CMS\Model\VirtualPage:
11+
extensions:
12+
- Wilr\SilverStripe\Algolia\Extensions\VirtualPageExtension
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Wilr\SilverStripe\Algolia\Extensions;
4+
5+
use SilverStripe\Core\Injector\Injector;
6+
use SilverStripe\ORM\ArrayList;
7+
use SilverStripe\ORM\Map;
8+
use SilverStripe\Subsites\Model\Subsite;
9+
use Wilr\SilverStripe\Algolia\Service\AlgoliaIndexer;
10+
11+
class SubsitesVirtualPageExtension extends \SilverStripe\ORM\DataExtension
12+
{
13+
public function exportObjectToAlgolia($toIndex)
14+
{
15+
$attributes = new Map(ArrayList::create());
16+
17+
foreach ($toIndex as $k => $v) {
18+
if ($k === 'objectClassName') {
19+
continue;
20+
}
21+
22+
$attributes->push($k, $v);
23+
}
24+
25+
/** @var AlgoliaIndexer */
26+
$indexer = Injector::inst()->get(AlgoliaIndexer::class);
27+
$owner = $this->owner;
28+
29+
// get original object
30+
$result = Subsite::withDisabledSubsiteFilter(function () use ($owner, $attributes, $indexer) {
31+
$originalObject = $owner->CopyContentFrom();
32+
33+
if (!$originalObject) {
34+
return $attributes;
35+
}
36+
37+
$attributes->push('objectClassName', $originalObject->ClassName);
38+
$specs = $originalObject->config()->get('algolia_index_fields');
39+
$attributes = $indexer->addSpecsToAttributes($originalObject, $attributes, $specs);
40+
41+
$originalObject->invokeWithExtensions('updateAlgoliaAttributes', $attributes);
42+
43+
return $attributes;
44+
});
45+
46+
return $result;
47+
}
48+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Wilr\SilverStripe\Algolia\Extensions;
4+
5+
use SilverStripe\Core\Injector\Injector;
6+
use SilverStripe\ORM\ArrayList;
7+
use SilverStripe\ORM\Map;
8+
use Wilr\SilverStripe\Algolia\Service\AlgoliaIndexer;
9+
10+
class VirtualPageExtension extends \SilverStripe\ORM\DataExtension
11+
{
12+
public function exportObjectToAlgolia($toIndex)
13+
{
14+
$attributes = new Map(ArrayList::create());
15+
16+
foreach ($toIndex as $k => $v) {
17+
if ($k === 'objectClassName') {
18+
continue;
19+
}
20+
21+
$attributes->push($k, $v);
22+
}
23+
24+
/** @var AlgoliaIndexer */
25+
$indexer = Injector::inst()->get(AlgoliaIndexer::class);
26+
$owner = $this->owner;
27+
28+
// get original object
29+
$originalObject = $owner->CopyContentFrom();
30+
31+
if (!$originalObject) {
32+
return $attributes;
33+
}
34+
35+
$attributes->push('objectClassName', $originalObject->ClassName);
36+
$specs = $originalObject->config()->get('algolia_index_fields');
37+
$attributes = $indexer->addSpecsToAttributes($originalObject, $attributes, $specs);
38+
39+
$originalObject->invokeWithExtensions('updateAlgoliaAttributes', $attributes);
40+
41+
return $attributes;
42+
}
43+
}

src/Service/AlgoliaIndexer.php

+91-71
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Wilr\SilverStripe\Algolia\Service;
44

55
use Algolia\AlgoliaSearch\Exceptions\NotFoundException;
6+
use Exception;
67
use LogicException;
78
use Psr\Log\LoggerInterface;
89
use SilverStripe\Core\Injector\Injector;
@@ -39,7 +40,11 @@ class AlgoliaIndexer
3940
* @config
4041
*/
4142
private static $attributes_blacklisted = [
42-
'ID', 'Title', 'ClassName', 'LastEdited', 'Created'
43+
'ID',
44+
'Title',
45+
'ClassName',
46+
'LastEdited',
47+
'Created'
4348
];
4449

4550
/**
@@ -61,7 +66,14 @@ class AlgoliaIndexer
6166
public function indexItem($item)
6267
{
6368
$searchIndexes = $this->getService()->initIndexes($item);
64-
$fields = $this->exportAttributesFromObject($item);
69+
70+
try {
71+
$fields = $this->exportAttributesFromObject($item);
72+
} catch (Exception $e) {
73+
Injector::inst()->get(LoggerInterface::class)->error($e);
74+
75+
return false;
76+
}
6577

6678
if (method_exists($fields, 'toArray')) {
6779
$fields = $fields->toArray();
@@ -196,86 +208,94 @@ public function exportAttributesFromObject($item)
196208
$specs = $item->config()->get('algolia_index_fields');
197209

198210
if ($specs) {
199-
$maxFieldSize = $this->config()->get('max_field_size_bytes');
211+
$attributes = $this->addSpecsToAttributes($item, $attributes, $specs);
212+
}
200213

201-
foreach ($specs as $attributeName) {
202-
if (in_array($attributeName, $this->config()->get('attributes_blacklisted'))) {
203-
continue;
204-
}
214+
$item->invokeWithExtensions('updateAlgoliaAttributes', $attributes);
205215

206-
// fetch the db object, or fallback to the getters but prefer
207-
// the db object
208-
try {
209-
$dbField = $item->relObject($attributeName);
210-
} catch (LogicException $e) {
211-
$dbField = $item->{$attributeName};
212-
}
216+
return $attributes;
217+
}
213218

214-
if (!$dbField) {
215-
continue;
216-
}
217219

218-
if (is_string($dbField) || is_array($dbField)) {
219-
$attributes->push($attributeName, $dbField);
220-
} elseif ($dbField instanceof DBForeignKey) {
221-
$attributes->push($attributeName, $dbField->Value);
222-
} elseif ($dbField->exists() || $dbField instanceof DBBoolean) {
223-
if ($dbField instanceof RelationList || $dbField instanceof DataObject) {
224-
// has-many, many-many, has-one
225-
$this->exportAttributesFromRelationship($item, $attributeName, $attributes);
226-
} else {
227-
// db-field, if it's a date then use the timestamp since we need it
228-
$hasContent = true;
229-
230-
switch (get_class($dbField)) {
231-
case DBDate::class:
232-
case DBDatetime::class:
233-
$value = $dbField->getTimestamp();
234-
break;
235-
case DBBoolean::class:
236-
$value = $dbField->getValue();
237-
break;
238-
case DBHTMLText::class:
239-
$fieldData = $dbField->Plain();
240-
$fieldLength = mb_strlen($fieldData, '8bit');
241-
242-
if ($fieldLength > $maxFieldSize) {
243-
$maxIterations = 100;
244-
$i = 0;
245-
246-
while ($hasContent && $i < $maxIterations) {
247-
$block = mb_strcut(
248-
$fieldData,
249-
$i * $maxFieldSize,
250-
$maxFieldSize - 1
251-
);
252-
253-
if ($block) {
254-
$attributes->push($attributeName . '_Block' . $i, $block);
255-
} else {
256-
$hasContent = false;
257-
}
258-
259-
$i++;
220+
public function addSpecsToAttributes($item, $attributes, $specs)
221+
{
222+
$maxFieldSize = $this->config()->get('max_field_size_bytes');
223+
224+
foreach ($specs as $attributeName) {
225+
if (in_array($attributeName, $this->config()->get('attributes_blacklisted'))) {
226+
continue;
227+
}
228+
229+
// fetch the db object, or fallback to the getters but prefer
230+
// the db object
231+
try {
232+
$dbField = $item->relObject($attributeName);
233+
} catch (LogicException $e) {
234+
$dbField = $item->{$attributeName};
235+
}
236+
237+
if (!$dbField) {
238+
continue;
239+
}
240+
241+
if (is_string($dbField) || is_array($dbField)) {
242+
$attributes->push($attributeName, $dbField);
243+
} elseif ($dbField instanceof DBForeignKey) {
244+
$attributes->push($attributeName, $dbField->Value);
245+
} elseif ($dbField->exists() || $dbField instanceof DBBoolean) {
246+
if ($dbField instanceof RelationList || $dbField instanceof DataObject) {
247+
// has-many, many-many, has-one
248+
$this->exportAttributesFromRelationship($item, $attributeName, $attributes);
249+
} else {
250+
// db-field, if it's a date then use the timestamp since we need it
251+
$hasContent = true;
252+
253+
switch (get_class($dbField)) {
254+
case DBDate::class:
255+
case DBDatetime::class:
256+
$value = $dbField->getTimestamp();
257+
break;
258+
case DBBoolean::class:
259+
$value = $dbField->getValue();
260+
break;
261+
case DBHTMLText::class:
262+
$fieldData = $dbField->Plain();
263+
$fieldLength = mb_strlen($fieldData, '8bit');
264+
265+
if ($fieldLength > $maxFieldSize) {
266+
$maxIterations = 100;
267+
$i = 0;
268+
269+
while ($hasContent && $i < $maxIterations) {
270+
$block = mb_strcut(
271+
$fieldData,
272+
$i * $maxFieldSize,
273+
$maxFieldSize - 1
274+
);
275+
276+
if ($block) {
277+
$attributes->push($attributeName . '_Block' . $i, $block);
278+
} else {
279+
$hasContent = false;
260280
}
261-
} else {
262-
$value = $fieldData;
281+
282+
$i++;
263283
}
264-
break;
265-
default:
266-
$value = @$dbField->forTemplate();
267-
}
268-
269-
if ($hasContent) {
270-
$attributes->push($attributeName, $value);
271-
}
284+
} else {
285+
$value = $fieldData;
286+
}
287+
break;
288+
default:
289+
$value = @$dbField->forTemplate();
290+
}
291+
292+
if ($hasContent) {
293+
$attributes->push($attributeName, $value);
272294
}
273295
}
274296
}
275297
}
276298

277-
$item->invokeWithExtensions('updateAlgoliaAttributes', $attributes);
278-
279299
return $attributes;
280300
}
281301

0 commit comments

Comments
 (0)