Skip to content

Commit e04ebdc

Browse files
Merge pull request #1 from aligent/feature/BEG-81_initial_version
Feature/beg 81 initial version
2 parents 9f3758c + 06d6cd6 commit e04ebdc

File tree

16 files changed

+1215
-1
lines changed

16 files changed

+1215
-1
lines changed

.github/workflows/php.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
name: Run PHP Static Analysis
22

3-
on: pull_request
3+
on:
4+
pull_request:
5+
branches-ignore:
6+
- '**'
47

58
jobs:
69
code-standards:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/.idea

Api/PrerenderClientInterface.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
/*
3+
* Copyright (c) Aligent Consulting. All rights reserved.
4+
*/
5+
6+
declare(strict_types=1);
7+
namespace Aligent\PrerenderIo\Api;
8+
9+
interface PrerenderClientInterface
10+
{
11+
public function recacheUrls(array $urls, int $storeId): void;
12+
}

Helper/Config.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
/*
3+
* Copyright (c) Aligent Consulting. All rights reserved.
4+
*/
5+
6+
declare(strict_types=1);
7+
namespace Aligent\PrerenderIo\Helper;
8+
9+
use Magento\Framework\App\Config\ScopeConfigInterface;
10+
use Magento\Store\Model\ScopeInterface;
11+
12+
class Config
13+
{
14+
private const XML_PATH_RECACHE_ENABLED = 'system/prerender_io/enabled';
15+
private const XML_PATH_PRERENDER_TOKEN = 'system/prerender_io/token';
16+
17+
/** @var ScopeConfigInterface */
18+
private ScopeConfigInterface $scopeConfig;
19+
20+
/**
21+
*
22+
* @param ScopeConfigInterface $scopeConfig
23+
*/
24+
public function __construct(ScopeConfigInterface $scopeConfig)
25+
{
26+
$this->scopeConfig = $scopeConfig;
27+
}
28+
29+
/**
30+
* Return if recaching functionality is enabled or not
31+
*
32+
* @param int|null $storeId
33+
* @return bool
34+
*/
35+
public function isRecacheEnabled(?int $storeId = null): bool
36+
{
37+
return $this->scopeConfig->isSetFlag(
38+
self::XML_PATH_RECACHE_ENABLED,
39+
ScopeInterface::SCOPE_STORE,
40+
$storeId
41+
);
42+
}
43+
44+
/**
45+
* Return configured Prerender.io token for API calls
46+
*
47+
* @param int|null $storeId
48+
* @return string|null
49+
*/
50+
public function getToken(?int $storeId = null): ?string
51+
{
52+
return $this->scopeConfig->getValue(
53+
self::XML_PATH_PRERENDER_TOKEN,
54+
ScopeInterface::SCOPE_STORE,
55+
$storeId
56+
);
57+
}
58+
}

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

Model/Api/PrerenderClient.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
/*
3+
* Copyright (c) Aligent Consulting. All rights reserved.
4+
*/
5+
6+
declare(strict_types=1);
7+
namespace Aligent\PrerenderIo\Model\Api;
8+
9+
use Aligent\PrerenderIo\Api\PrerenderClientInterface;
10+
use Aligent\PrerenderIo\Helper\Config;
11+
use Magento\Framework\HTTP\ClientInterface;
12+
use Magento\Framework\Serialize\SerializerInterface;
13+
use Psr\Log\LoggerInterface;
14+
15+
class PrerenderClient implements PrerenderClientInterface
16+
{
17+
18+
private const MAX_URLS = 1000;
19+
private const PRERENDER_RECACHE_URL = 'https://api.prerender.io/recache';
20+
21+
/** @var Config */
22+
private Config $prerenderConfigHelper;
23+
/** @var ClientInterface */
24+
private ClientInterface $client;
25+
/** @var SerializerInterface */
26+
private SerializerInterface $jsonSerializer;
27+
/** @var LoggerInterface */
28+
private LoggerInterface $logger;
29+
30+
/**
31+
*
32+
* @param Config $prerenderConfigHelper
33+
* @param ClientInterface $client
34+
* @param SerializerInterface $jsonSerializer
35+
* @param LoggerInterface $logger
36+
*/
37+
public function __construct(
38+
Config $prerenderConfigHelper,
39+
ClientInterface $client,
40+
SerializerInterface $jsonSerializer,
41+
LoggerInterface $logger
42+
) {
43+
$this->prerenderConfigHelper = $prerenderConfigHelper;
44+
$this->jsonSerializer = $jsonSerializer;
45+
$this->logger = $logger;
46+
$this->client = $client;
47+
$this->client->addHeader('content-type', 'application/json');
48+
}
49+
50+
/**
51+
* Call Prerender.io API to recache list of URLs
52+
*
53+
* @param array $urls
54+
* @param int $storeId
55+
* @return void
56+
*/
57+
public function recacheUrls(array $urls, int $storeId): void
58+
{
59+
if (!$this->prerenderConfigHelper->isRecacheEnabled($storeId)) {
60+
return;
61+
}
62+
$token = $this->prerenderConfigHelper->getToken($storeId);
63+
if (empty($token)) {
64+
$this->logger->info(__('Prerender token is not set'));
65+
return;
66+
}
67+
68+
$batches = array_chunk($urls, self::MAX_URLS);
69+
foreach ($batches as $batch) {
70+
$this->sendRequest($batch, $token);
71+
}
72+
}
73+
74+
/**
75+
* Sends a JSON POST request to Prerender.io
76+
*
77+
* @param array $urls
78+
* @param string $token
79+
* @return void
80+
*/
81+
private function sendRequest(array $urls, string $token): void
82+
{
83+
$payload = $this->jsonSerializer->serialize(
84+
[
85+
'prerenderToken' => $token,
86+
'urls' => $urls
87+
]
88+
);
89+
try {
90+
$this->client->post(self::PRERENDER_RECACHE_URL, $payload);
91+
} catch (\Exception $e) {
92+
$this->logger->error(
93+
__('Error sending payload %1 to prerender.io', $payload),
94+
['exception' => $e]
95+
);
96+
}
97+
}
98+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?php
2+
/*
3+
* Copyright (c) Aligent Consulting. All rights reserved.
4+
*/
5+
6+
declare(strict_types=1);
7+
8+
namespace Aligent\PrerenderIo\Model\Indexer\Product;
9+
10+
use Aligent\PrerenderIo\Api\PrerenderClientInterface;
11+
use Aligent\PrerenderIo\Helper\Config;
12+
use Aligent\PrerenderIo\Model\Product\GetUrlsForProducts;
13+
use Magento\Framework\App\DeploymentConfig;
14+
use Magento\Framework\Exception\FileSystemException;
15+
use Magento\Framework\Exception\LocalizedException;
16+
use Magento\Framework\Exception\RuntimeException;
17+
use Magento\Framework\Indexer\ActionInterface as IndexerActionInterface;
18+
use Magento\Framework\Indexer\DimensionalIndexerInterface;
19+
use Magento\Framework\Indexer\DimensionProviderInterface;
20+
use Magento\Framework\Mview\ActionInterface as MviewActionInterface;
21+
use Magento\Store\Model\StoreDimensionProvider;
22+
23+
class ProductIndexer implements IndexerActionInterface, MviewActionInterface, DimensionalIndexerInterface
24+
{
25+
private const INDEXER_ID = 'catalogsearch_fulltext';
26+
private const DEPLOYMENT_CONFIG_INDEXER_BATCHES = 'indexer/batch_size/';
27+
28+
/** @var DimensionProviderInterface */
29+
private DimensionProviderInterface $dimensionProvider;
30+
/** @var GetUrlsForProducts */
31+
private GetUrlsForProducts $getUrlsForProducts;
32+
/** @var PrerenderClientInterface */
33+
private PrerenderClientInterface $prerenderClient;
34+
/** @var DeploymentConfig */
35+
private DeploymentConfig $eploymentConfig;
36+
/** @var Config */
37+
private Config $prerenderConfigHelper;
38+
/** @var int|null */
39+
private ?int $batchSize;
40+
41+
/**
42+
*
43+
* @param DimensionProviderInterface $dimensionProvider
44+
* @param GetUrlsForProducts $getUrlsForProducts
45+
* @param PrerenderClientInterface $prerenderClient
46+
* @param DeploymentConfig $deploymentConfig
47+
* @param Config $prerenderConfigHelper
48+
* @param int|null $batchSize
49+
*/
50+
public function __construct(
51+
DimensionProviderInterface $dimensionProvider,
52+
GetUrlsForProducts $getUrlsForProducts,
53+
PrerenderClientInterface $prerenderClient,
54+
DeploymentConfig $deploymentConfig,
55+
Config $prerenderConfigHelper,
56+
?int $batchSize = 1000
57+
) {
58+
$this->dimensionProvider = $dimensionProvider;
59+
$this->getUrlsForProducts = $getUrlsForProducts;
60+
$this->prerenderClient = $prerenderClient;
61+
$this->deploymentConfig = $deploymentConfig;
62+
$this->batchSize = $batchSize;
63+
$this->prerenderConfigHelper = $prerenderConfigHelper;
64+
}
65+
66+
/**
67+
* Execute full indexation
68+
*
69+
* @return void
70+
*/
71+
public function executeFull(): void
72+
{
73+
$this->executeList([]);
74+
}
75+
76+
/**
77+
* Execute partial indexation by ID list
78+
*
79+
* @param int[] $ids
80+
* @return void
81+
*/
82+
public function executeList(array $ids): void
83+
{
84+
foreach ($this->dimensionProvider->getIterator() as $dimension) {
85+
try {
86+
$this->executeByDimensions($dimension, new \ArrayIterator($ids));
87+
} catch (FileSystemException|RuntimeException $e) {
88+
continue;
89+
}
90+
}
91+
}
92+
93+
/**
94+
* Execute partial indexation by ID
95+
*
96+
* @param int $id
97+
* @return void
98+
* @throws LocalizedException
99+
*/
100+
public function executeRow($id): void
101+
{
102+
if (!$id) {
103+
throw new LocalizedException(
104+
__('Cannot recache url for an undefined product.')
105+
);
106+
}
107+
$this->executeList([$id]);
108+
}
109+
110+
/**
111+
* Execute materialization on ids entities
112+
*
113+
* @param int[] $ids
114+
* @return void
115+
*/
116+
public function execute($ids): void
117+
{
118+
$this->executeList($ids);
119+
}
120+
121+
/**
122+
* Execute indexing per dimension (store)
123+
*
124+
* @param arry $dimensions
125+
* @param \Traversable $entityIds
126+
* @throws FileSystemException
127+
* @throws RuntimeException
128+
*/
129+
public function executeByDimensions(array $dimensions, \Traversable $entityIds): void
130+
{
131+
if (count($dimensions) > 1 || !isset($dimensions[StoreDimensionProvider::DIMENSION_NAME])) {
132+
throw new \InvalidArgumentException('Indexer "' . self::INDEXER_ID . '" supports only Store dimension');
133+
}
134+
$storeId = (int)$dimensions[StoreDimensionProvider::DIMENSION_NAME]->getValue();
135+
136+
if (!$this->prerenderConfigHelper->isRecacheEnabled($storeId)) {
137+
return;
138+
}
139+
140+
$entityIds = iterator_to_array($entityIds);
141+
// get urls for the products
142+
$urls = $this->getUrlsForProducts->execute($entityIds, $storeId);
143+
144+
$this->batchSize = $this->deploymentConfig->get(
145+
self::DEPLOYMENT_CONFIG_INDEXER_BATCHES . self::INDEXER_ID . '/partial_reindex'
146+
) ?? $this->batchSize;
147+
148+
$urlBatches = array_chunk($urls, $this->batchSize);
149+
foreach ($urlBatches as $batchUrls) {
150+
$this->prerenderClient->recacheUrls($batchUrls, $storeId);
151+
}
152+
}
153+
}

0 commit comments

Comments
 (0)