-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from aligent/feature/BEG-81_category_indexing
BEG-81: Add re-caching of category URLs
- Loading branch information
Showing
10 changed files
with
521 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
<?php | ||
/* | ||
* Copyright (c) Aligent Consulting. All rights reserved. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Aligent\PrerenderIo\Model\Indexer\Category; | ||
|
||
use Aligent\PrerenderIo\Api\PrerenderClientInterface; | ||
use Aligent\PrerenderIo\Helper\Config; | ||
use Aligent\PrerenderIo\Model\Url\GetUrlsForCategories; | ||
use Magento\Framework\App\DeploymentConfig; | ||
use Magento\Framework\Exception\FileSystemException; | ||
use Magento\Framework\Exception\LocalizedException; | ||
use Magento\Framework\Exception\RuntimeException; | ||
use Magento\Framework\Indexer\ActionInterface as IndexerActionInterface; | ||
use Magento\Framework\Indexer\DimensionalIndexerInterface; | ||
use Magento\Framework\Indexer\DimensionProviderInterface; | ||
use Magento\Framework\Mview\ActionInterface as MviewActionInterface; | ||
use Magento\Store\Model\StoreDimensionProvider; | ||
|
||
class CategoryIndexer implements IndexerActionInterface, MviewActionInterface, DimensionalIndexerInterface | ||
{ | ||
private const INDEXER_ID = 'prerender_io_category'; | ||
private const DEPLOYMENT_CONFIG_INDEXER_BATCHES = 'indexer/batch_size/'; | ||
|
||
/** @var DimensionProviderInterface */ | ||
private DimensionProviderInterface $dimensionProvider; | ||
/** @var GetUrlsForCategories */ | ||
private GetUrlsForCategories $getUrlsForCategories; | ||
/** @var PrerenderClientInterface */ | ||
private PrerenderClientInterface $prerenderClient; | ||
/** @var DeploymentConfig */ | ||
private DeploymentConfig $eploymentConfig; | ||
/** @var Config */ | ||
private Config $prerenderConfigHelper; | ||
/** @var int|null */ | ||
private ?int $batchSize; | ||
|
||
/** | ||
* | ||
* @param DimensionProviderInterface $dimensionProvider | ||
* @param GetUrlsForCategories $getUrlsForCategories | ||
* @param PrerenderClientInterface $prerenderClient | ||
* @param DeploymentConfig $deploymentConfig | ||
* @param Config $prerenderConfigHelper | ||
* @param int|null $batchSize | ||
*/ | ||
public function __construct( | ||
DimensionProviderInterface $dimensionProvider, | ||
GetUrlsForCategories $getUrlsForCategories, | ||
PrerenderClientInterface $prerenderClient, | ||
DeploymentConfig $deploymentConfig, | ||
Config $prerenderConfigHelper, | ||
?int $batchSize = 1000 | ||
) { | ||
$this->dimensionProvider = $dimensionProvider; | ||
$this->getUrlsForCategories = $getUrlsForCategories; | ||
$this->prerenderClient = $prerenderClient; | ||
$this->deploymentConfig = $deploymentConfig; | ||
$this->batchSize = $batchSize; | ||
$this->prerenderConfigHelper = $prerenderConfigHelper; | ||
} | ||
|
||
/** | ||
* Execute full indexation | ||
* | ||
* @return void | ||
*/ | ||
public function executeFull(): void | ||
{ | ||
$this->executeList([]); | ||
} | ||
|
||
/** | ||
* Execute partial indexation by ID list | ||
* | ||
* @param int[] $ids | ||
* @return void | ||
*/ | ||
public function executeList(array $ids): void | ||
{ | ||
foreach ($this->dimensionProvider->getIterator() as $dimension) { | ||
try { | ||
$this->executeByDimensions($dimension, new \ArrayIterator($ids)); | ||
} catch (FileSystemException|RuntimeException $e) { | ||
continue; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Execute partial indexation by ID | ||
* | ||
* @param int $id | ||
* @return void | ||
* @throws LocalizedException | ||
*/ | ||
public function executeRow($id): void | ||
{ | ||
if (!$id) { | ||
throw new LocalizedException( | ||
__('Cannot recache url for an undefined product.') | ||
); | ||
} | ||
$this->executeList([$id]); | ||
} | ||
|
||
/** | ||
* Execute materialization on ids entities | ||
* | ||
* @param int[] $ids | ||
* @return void | ||
*/ | ||
public function execute($ids): void | ||
{ | ||
$this->executeList($ids); | ||
} | ||
|
||
/** | ||
* Execute indexing per dimension (store) | ||
* | ||
* @param arry $dimensions | ||
* @param \Traversable $entityIds | ||
* @throws FileSystemException | ||
* @throws RuntimeException | ||
*/ | ||
public function executeByDimensions(array $dimensions, \Traversable $entityIds): void | ||
{ | ||
if (count($dimensions) > 1 || !isset($dimensions[StoreDimensionProvider::DIMENSION_NAME])) { | ||
throw new \InvalidArgumentException('Indexer "' . self::INDEXER_ID . '" supports only Store dimension'); | ||
} | ||
$storeId = (int)$dimensions[StoreDimensionProvider::DIMENSION_NAME]->getValue(); | ||
|
||
if (!$this->prerenderConfigHelper->isRecacheEnabled($storeId)) { | ||
return; | ||
} | ||
|
||
$entityIds = iterator_to_array($entityIds); | ||
// get urls for the products | ||
$urls = $this->getUrlsForCategories->execute($entityIds, $storeId); | ||
|
||
$this->batchSize = $this->deploymentConfig->get( | ||
self::DEPLOYMENT_CONFIG_INDEXER_BATCHES . self::INDEXER_ID . '/partial_reindex' | ||
) ?? $this->batchSize; | ||
|
||
$urlBatches = array_chunk($urls, $this->batchSize); | ||
foreach ($urlBatches as $batchUrls) { | ||
$this->prerenderClient->recacheUrls($batchUrls, $storeId); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
<?php | ||
/* | ||
* Copyright (c) Aligent Consulting. All rights reserved. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Aligent\PrerenderIo\Model\Indexer\Category; | ||
|
||
use Aligent\PrerenderIo\Api\PrerenderClientInterface; | ||
use Aligent\PrerenderIo\Helper\Config; | ||
use Aligent\PrerenderIo\Model\Indexer\DataProvider\ProductCategories; | ||
use Aligent\PrerenderIo\Model\Url\GetUrlsForCategories; | ||
use Magento\Framework\App\DeploymentConfig; | ||
use Magento\Framework\Exception\FileSystemException; | ||
use Magento\Framework\Exception\LocalizedException; | ||
use Magento\Framework\Exception\RuntimeException; | ||
use Magento\Framework\Indexer\ActionInterface as IndexerActionInterface; | ||
use Magento\Framework\Indexer\DimensionalIndexerInterface; | ||
use Magento\Framework\Indexer\DimensionProviderInterface; | ||
use Magento\Framework\Mview\ActionInterface as MviewActionInterface; | ||
use Magento\Store\Model\StoreDimensionProvider; | ||
|
||
class ProductIndexer implements IndexerActionInterface, MviewActionInterface, DimensionalIndexerInterface | ||
{ | ||
private const INDEXER_ID = 'prerender_io_category_product'; | ||
private const DEPLOYMENT_CONFIG_INDEXER_BATCHES = 'indexer/batch_size/'; | ||
|
||
/** @var DimensionProviderInterface */ | ||
private DimensionProviderInterface $dimensionProvider; | ||
/** @var ProductCategories */ | ||
private ProductCategories $productCategoriesDataProvider; | ||
/** @var GetUrlsForCategories */ | ||
private GetUrlsForCategories $getUrlsForCategories; | ||
/** @var PrerenderClientInterface */ | ||
private PrerenderClientInterface $prerenderClient; | ||
/** @var DeploymentConfig */ | ||
private DeploymentConfig $eploymentConfig; | ||
/** @var Config */ | ||
private Config $prerenderConfigHelper; | ||
/** @var int|null */ | ||
private ?int $batchSize; | ||
|
||
/** | ||
* | ||
* @param DimensionProviderInterface $dimensionProvider | ||
* @param ProductCategories $productCategoriesDataProvider | ||
* @param GetUrlsForCategories $getUrlsForCategories | ||
* @param PrerenderClientInterface $prerenderClient | ||
* @param DeploymentConfig $deploymentConfig | ||
* @param Config $prerenderConfigHelper | ||
* @param int|null $batchSize | ||
*/ | ||
public function __construct( | ||
DimensionProviderInterface $dimensionProvider, | ||
ProductCategories $productCategoriesDataProvider, | ||
GetUrlsForCategories $getUrlsForCategories, | ||
PrerenderClientInterface $prerenderClient, | ||
DeploymentConfig $deploymentConfig, | ||
Config $prerenderConfigHelper, | ||
?int $batchSize = 1000 | ||
) { | ||
$this->dimensionProvider = $dimensionProvider; | ||
$this->productCategoriesDataProvider = $productCategoriesDataProvider; | ||
$this->getUrlsForCategories = $getUrlsForCategories; | ||
$this->prerenderClient = $prerenderClient; | ||
$this->deploymentConfig = $deploymentConfig; | ||
$this->batchSize = $batchSize; | ||
$this->prerenderConfigHelper = $prerenderConfigHelper; | ||
} | ||
|
||
/** | ||
* Execute full indexation | ||
* | ||
* @return void | ||
*/ | ||
public function executeFull(): void | ||
{ | ||
$this->executeList([]); | ||
} | ||
|
||
/** | ||
* Execute partial indexation by ID list | ||
* | ||
* @param int[] $ids | ||
* @return void | ||
*/ | ||
public function executeList(array $ids): void | ||
{ | ||
foreach ($this->dimensionProvider->getIterator() as $dimension) { | ||
try { | ||
$this->executeByDimensions($dimension, new \ArrayIterator($ids)); | ||
} catch (FileSystemException|RuntimeException $e) { | ||
continue; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Execute partial indexation by ID | ||
* | ||
* @param int $id | ||
* @return void | ||
* @throws LocalizedException | ||
*/ | ||
public function executeRow($id): void | ||
{ | ||
if (!$id) { | ||
throw new LocalizedException( | ||
__('Cannot recache url for an undefined product.') | ||
); | ||
} | ||
$this->executeList([$id]); | ||
} | ||
|
||
/** | ||
* Execute materialization on ids entities | ||
* | ||
* @param int[] $ids | ||
* @return void | ||
*/ | ||
public function execute($ids): void | ||
{ | ||
$this->executeList($ids); | ||
} | ||
|
||
/** | ||
* Execute indexing per dimension (store) | ||
* | ||
* @param arry $dimensions | ||
* @param \Traversable $entityIds | ||
* @throws FileSystemException | ||
* @throws RuntimeException | ||
*/ | ||
public function executeByDimensions(array $dimensions, \Traversable $entityIds): void | ||
{ | ||
if (count($dimensions) > 1 || !isset($dimensions[StoreDimensionProvider::DIMENSION_NAME])) { | ||
throw new \InvalidArgumentException('Indexer "' . self::INDEXER_ID . '" supports only Store dimension'); | ||
} | ||
$storeId = (int)$dimensions[StoreDimensionProvider::DIMENSION_NAME]->getValue(); | ||
|
||
if (!$this->prerenderConfigHelper->isRecacheEnabled($storeId)) { | ||
return; | ||
} | ||
|
||
$entityIds = iterator_to_array($entityIds); | ||
// get list of category ids for the products | ||
$categoryIds = $this->productCategoriesDataProvider->getCategoryIdsForProducts($entityIds, $storeId); | ||
|
||
// get urls for the products | ||
$urls = $this->getUrlsForCategories->execute($categoryIds, $storeId); | ||
|
||
$this->batchSize = $this->deploymentConfig->get( | ||
self::DEPLOYMENT_CONFIG_INDEXER_BATCHES . self::INDEXER_ID . '/partial_reindex' | ||
) ?? $this->batchSize; | ||
|
||
$urlBatches = array_chunk($urls, $this->batchSize); | ||
foreach ($urlBatches as $batchUrls) { | ||
$this->prerenderClient->recacheUrls($batchUrls, $storeId); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?php | ||
/* | ||
* Copyright (c) Aligent Consulting. All rights reserved. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Aligent\PrerenderIo\Model\Indexer\DataProvider; | ||
|
||
use Magento\Catalog\Model\Product; | ||
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory; | ||
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; | ||
|
||
class ProductCategories | ||
{ | ||
/** @var ProductCollectionFactory */ | ||
private ProductCollectionFactory $productCollectionFactory; | ||
/** @var CategoryCollectionFactory */ | ||
private CategoryCollectionFactory $categoryCollectionFactory; | ||
|
||
/** | ||
* @param ProductCollectionFactory $productCollectionFactory | ||
* @param CategoryCollectionFactory $categoryCollectionFactory | ||
*/ | ||
public function __construct( | ||
ProductCollectionFactory $productCollectionFactory, | ||
CategoryCollectionFactory $categoryCollectionFactory | ||
) { | ||
$this->productCollectionFactory = $productCollectionFactory; | ||
$this->categoryCollectionFactory = $categoryCollectionFactory; | ||
} | ||
|
||
/** | ||
* Get complete list of categories containing one or more of the given products | ||
* | ||
* @param array $productIds | ||
* @param int $storeId | ||
* @return array | ||
*/ | ||
public function getCategoryIdsForProducts(array $productIds, int $storeId): array | ||
{ | ||
// if array of product ids is empty, just load all categories | ||
if (empty($productIds)) { | ||
$categoryCollection = $this->categoryCollectionFactory->create(); | ||
$categoryCollection->setStoreId($storeId); | ||
return $categoryCollection->getAllIds(); | ||
} | ||
|
||
$productCollection = $this->productCollectionFactory->create(); | ||
$productCollection->addIdFilter($productIds); | ||
$productCollection->setStoreId($storeId); | ||
// add category information | ||
$productCollection->addCategoryIds(); | ||
|
||
$categoryIds = []; | ||
/** @var Product $product */ | ||
foreach ($productCollection->getItems() as $product) { | ||
$categoryIds[] = $product->getCategoryIds(); | ||
} | ||
return array_unique(array_merge(...$categoryIds)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.