Skip to content

Commit d16b3b9

Browse files
authored
DCAT-466: Eliminate SaaS dependency on Adobe Commerce Price Indexer - Part 2 (#245)
* DCAT-466: (#75) - Eliminate SaaS dependency on Adobe Commerce Price Indexer - Part 2
1 parent 9f22a65 commit d16b3b9

35 files changed

+943
-232
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Mview/etc/mview.xsd">
9+
<view id="catalog_data_exporter_products" class="Magento\CatalogDataExporter\Model\Indexer\ProductFeedIndexer" group="indexer">
10+
<subscriptions>
11+
<!-- used to collect option "type" and "required" attributes-->
12+
<table name="catalog_product_bundle_option" entity_column="parent_id" />
13+
<!-- used to collect option "title" attribute-->
14+
<table name="catalog_product_bundle_option_value" entity_column="parent_product_id" />
15+
<!-- used to collect option selection "qty" and "position" attribute-->
16+
<table name="catalog_product_bundle_selection" entity_column="parent_product_id" />
17+
</subscriptions>
18+
</view>
19+
</config>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\CatalogDataExporter\Plugin;
8+
9+
use Magento\Setup\Model\FixtureGenerator\SqlCollector;
10+
11+
/**
12+
* Filter out changelog tables by pattern {*_cl}: fixture generation running in Update on Schedule mode,
13+
* however it doesn't have knowledge how to hande changelog table
14+
*/
15+
class FilterChangeLogTable
16+
{
17+
/**
18+
* Filter out changelog tables by pattern {*_cl}
19+
*
20+
* @param SqlCollector $subject
21+
* @param array $result
22+
* @return array
23+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
24+
*/
25+
public function afterGetSql(SqlCollector $subject, array $result): array
26+
{
27+
return array_filter($result, static function ($item) {
28+
return !str_ends_with($item[1], '_cl');
29+
});
30+
}
31+
}

CatalogDataExporter/etc/di.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,10 @@
514514
<type name="Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcher">
515515
<plugin name="data-exporter-mode-price-view" type="Magento\CatalogDataExporter\Plugin\Index\CreateViewAfterSwitchDimensionMode" />
516516
</type>
517-
<type name="\Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcherConfiguration">
517+
<type name="Magento\Catalog\Model\Indexer\Product\Price\ModeSwitcherConfiguration">
518518
<plugin name="data-exporter-config-price-view" type="Magento\CatalogDataExporter\Plugin\Index\SaveNewPriceIndexerMode" />
519519
</type>
520+
<type name="Magento\Setup\Model\FixtureGenerator\SqlCollector">
521+
<plugin name="exporter-filter-changelog-table" type="Magento\CatalogDataExporter\Plugin\FilterChangeLogTable"/>
522+
</type>
520523
</config>

CatalogDataExporter/etc/et_schema.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
<using field="productId" />
7979
<using field="storeViewCode" />
8080
</field>
81+
<!-- buyable = [product status is "Enabled"] && [<product is In Stock] -->
8182
<field name="buyable" type="Boolean"
8283
provider="Magento\CatalogDataExporter\Model\Provider\Product\Buyable"
8384
>

CatalogDataExporter/etc/mview.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<view id="catalog_data_exporter_products" class="Magento\CatalogDataExporter\Model\Indexer\ProductFeedIndexer" group="indexer">
1010
<subscriptions>
1111
<table name="catalog_product_entity" entity_column="entity_id" />
12+
<table name="catalog_product_website" entity_column="product_id" />
1213
<table name="catalog_product_entity_gallery" entity_column="entity_id" />
1314
<table name="catalog_product_entity_int" entity_column="entity_id" />
1415
<table name="catalog_product_entity_media_gallery_value" entity_column="entity_id" />
@@ -20,6 +21,8 @@
2021
<table name="catalog_product_entity_tier_price" entity_column="entity_id" />
2122
<table name="catalogrule_product_price" entity_column="product_id" />
2223
<table name="catalog_category_product" entity_column="product_id" />
24+
<!-- parent/child relationship. Trigger update for child product only -->
25+
<table name="catalog_product_relation" entity_column="child_id" />
2326
</subscriptions>
2427
</view>
2528
<view id="catalog_data_exporter_product_attributes" class="Magento\CatalogDataExporter\Model\Indexer\ProductAttributeFeedIndex" group="indexer">

CatalogInventoryDataExporter/Model/Plugin/Buyable.php

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,30 @@
88
namespace Magento\CatalogInventoryDataExporter\Model\Plugin;
99

1010
use Magento\CatalogDataExporter\Model\Provider\Product\Buyable as ProductBuyable;
11-
use Magento\CatalogInventoryDataExporter\Model\Query\CatalogInventoryQuery;
11+
use Magento\CatalogInventoryDataExporter\Model\Provider\Product\InventoryDataProvider;
1212
use Magento\DataExporter\Exception\UnableRetrieveData;
13-
use Magento\Framework\App\ResourceConnection;
1413
use Magento\DataExporter\Model\Logging\CommerceDataExportLoggerInterface as LoggerInterface;
1514

1615
/**
1716
* Plugin for fetching products stock status and marking out of stock products
1817
*/
1918
class Buyable
2019
{
21-
/**
22-
* @var ResourceConnection
23-
*/
24-
private $resourceConnection;
20+
private LoggerInterface $logger;
2521

26-
/**
27-
* @var LoggerInterface
28-
*/
29-
private $logger;
30-
31-
/**
32-
* @var CatalogInventoryQuery
33-
*/
34-
private $catalogInventoryQuery;
22+
private InventoryDataProvider $inventoryDataProvider;
3523

3624
/**
37-
* @param ResourceConnection $resourceConnection
38-
* @param CatalogInventoryQuery $catalogInventoryQuery
3925
* @param LoggerInterface $logger
26+
* @param InventoryDataProvider $inventoryDataProvider
4027
*/
4128
public function __construct(
42-
ResourceConnection $resourceConnection,
43-
CatalogInventoryQuery $catalogInventoryQuery,
44-
LoggerInterface $logger
29+
LoggerInterface $logger,
30+
InventoryDataProvider $inventoryDataProvider
4531
) {
46-
$this->resourceConnection = $resourceConnection;
47-
$this->catalogInventoryQuery = $catalogInventoryQuery;
4832
$this->logger = $logger;
33+
$this->inventoryDataProvider = $inventoryDataProvider;
4934
}
50-
5135
/**
5236
* Check stock status after getting buyable product status
5337
*
@@ -62,23 +46,15 @@ public function __construct(
6246
*/
6347
public function afterGet(ProductBuyable $subject, array $result)
6448
{
65-
$connection = $this->resourceConnection->getConnection();
66-
$queryArguments = [];
6749
try {
68-
foreach ($result as $value) {
69-
$queryArguments['productId'][$value['productId']] = $value['productId'];
70-
$queryArguments['storeViewCode'][$value['storeViewCode']] = $value['storeViewCode'];
71-
}
72-
$select = $this->catalogInventoryQuery->getInStock($queryArguments);
73-
$cursor = $connection->query($select);
7450
$outOfStock = [];
75-
while ($row = $cursor->fetch()) {
76-
if ($row['is_in_stock'] == 0) {
77-
$outOfStock[$row['product_id']] = false;
51+
foreach ($this->inventoryDataProvider->get($result) as $stockItem) {
52+
if (!$stockItem['inStock']) {
53+
$outOfStock[$this->getKey($stockItem)] = true;
7854
}
7955
}
8056
foreach ($result as &$item) {
81-
if (isset($outOfStock[$item['productId']])) {
57+
if (isset($outOfStock[$this->getKey($item)])) {
8258
$item['buyable'] = false;
8359
}
8460
}
@@ -88,4 +64,13 @@ public function afterGet(ProductBuyable $subject, array $result)
8864
}
8965
return $result;
9066
}
67+
68+
/**
69+
* @param mixed $stockItem
70+
* @return string
71+
*/
72+
private function getKey(mixed $stockItem): string
73+
{
74+
return $stockItem['storeViewCode'] . '_' . $stockItem['productId'];
75+
}
9176
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\CatalogInventoryDataExporter\Model\Plugin;
8+
9+
use Magento\Indexer\Model\IndexerFactory;
10+
use Magento\InventoryIndexer\Model\ResourceModel\UpdateLegacyStockStatus;
11+
12+
/**
13+
* Covers case when Stock Status for Legacy Inventory has been changed after placing order
14+
* Note: consumer "inventory.reservations.updateSalabilityStatus" should be running
15+
*/
16+
class LegacyStockStatusUpdater
17+
{
18+
private ScheduleProductUpdate $scheduleProductUpdate;
19+
20+
/**
21+
* @param ScheduleProductUpdate $scheduleProductUpdate
22+
*/
23+
public function __construct(
24+
ScheduleProductUpdate $scheduleProductUpdate
25+
) {
26+
$this->scheduleProductUpdate = $scheduleProductUpdate;
27+
}
28+
29+
/**
30+
* @param UpdateLegacyStockStatus $subject
31+
* @param $result
32+
* @param array $dataForUpdate
33+
* @return void
34+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
35+
*/
36+
public function afterExecute(
37+
UpdateLegacyStockStatus $subject,
38+
$result,
39+
array $dataForUpdate
40+
): void {
41+
$this->scheduleProductUpdate->execute(array_keys($dataForUpdate));
42+
}
43+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\CatalogInventoryDataExporter\Model\Plugin;
8+
9+
use Magento\Framework\App\ResourceConnection;
10+
use Magento\Framework\Indexer\IndexerRegistry;
11+
use Magento\Indexer\Model\IndexerFactory;
12+
use Magento\DataExporter\Model\Logging\CommerceDataExportLoggerInterface as LoggerInterface;
13+
14+
/**
15+
* Schedule reindex for "product feed indexer" if Stock Status updated.
16+
* Out of the box we can't use standard mview.xml configuration to listen to changes on stock table
17+
* `inventory_stock_<stock_id>` since mview doesn't support subscribing on dynamic tables
18+
*/
19+
class ScheduleProductUpdate
20+
{
21+
private const FEED_INDEXER = 'catalog_data_exporter_products';
22+
23+
private IndexerRegistry $indexerRegistry;
24+
private IndexerFactory $indexerFactory;
25+
private ResourceConnection $resourceConnection;
26+
private LoggerInterface $logger;
27+
28+
/**
29+
* @param IndexerFactory $indexerFactory
30+
* @param IndexerRegistry $indexerRegistry
31+
* @param ResourceConnection $resourceConnection
32+
* @param LoggerInterface $logger
33+
*/
34+
public function __construct(
35+
IndexerFactory $indexerFactory,
36+
IndexerRegistry $indexerRegistry,
37+
ResourceConnection $resourceConnection,
38+
LoggerInterface $logger
39+
) {
40+
$this->indexerFactory = $indexerFactory;
41+
$this->indexerRegistry = $indexerRegistry;
42+
$this->resourceConnection = $resourceConnection;
43+
$this->logger = $logger;
44+
}
45+
46+
/**
47+
* Add product ids to changelog
48+
*
49+
* @param array $productSkus
50+
* @return void
51+
*/
52+
public function execute(array $productSkus): void
53+
{
54+
try {
55+
$productIndexer = $this->indexerRegistry->get(self::FEED_INDEXER);
56+
if (!empty($productSkus) && $productIndexer->isScheduled()) {
57+
$productIds = $this->getProductIdsFromSkus($productSkus);
58+
if (!$productIds) {
59+
$this->logger->warning("Cannot get product ids from SKUs: " . var_export($productSkus, true));
60+
return ;
61+
}
62+
$this->updateChangelog($productIds);
63+
}
64+
} catch (\Throwable $e) {
65+
$this->logger->error('Cannot update indexer during inventory source item save: ' . $e->getMessage());
66+
}
67+
}
68+
69+
/**
70+
* Update change log
71+
*
72+
* @param array $productIds
73+
* @return void
74+
*/
75+
private function updateChangelog(array $productIds): void
76+
{
77+
$connection = $this->resourceConnection->getConnection();
78+
$view = $this->indexerFactory->create()->load(self::FEED_INDEXER)->getView();
79+
$tableName = $view->getChangelog()->getName();
80+
$realTableName = $this->resourceConnection->getTableName($tableName);
81+
$connection->insertArray($realTableName, ['entity_id'], $productIds);
82+
}
83+
84+
/**
85+
* Get product ids from skus
86+
*
87+
* @param array $productSkus
88+
* @return array
89+
*/
90+
private function getProductIdsFromSkus(array $productSkus): array
91+
{
92+
$connection = $this->resourceConnection->getConnection();
93+
$select = $connection->select()
94+
->from(
95+
['e' => $this->resourceConnection->getTableName('catalog_product_entity')],
96+
['entity_id']
97+
)->where('sku IN (?)', $productSkus);
98+
99+
return $connection->fetchCol($select);
100+
}
101+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\CatalogInventoryDataExporter\Model\Plugin;
8+
9+
use Magento\InventoryApi\Api\Data\SourceItemInterface;
10+
use Magento\InventoryApi\Api\SourceItemsSaveInterface;
11+
12+
/**
13+
* Covers case when Source Item updated in the Admin Panel which may trigger Stock Status change
14+
*/
15+
class SourceItemUpdate
16+
{
17+
private ScheduleProductUpdate $scheduleProductUpdate;
18+
19+
/**
20+
* @param ScheduleProductUpdate $scheduleProductUpdate
21+
*/
22+
public function __construct(
23+
ScheduleProductUpdate $scheduleProductUpdate
24+
) {
25+
$this->scheduleProductUpdate = $scheduleProductUpdate;
26+
}
27+
28+
/**
29+
* @param SourceItemsSaveInterface $subject
30+
* @param null $result
31+
* @param SourceItemInterface[] $sourceItems
32+
* @return void
33+
*
34+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
35+
*/
36+
public function afterExecute(SourceItemsSaveInterface $subject, $result, array $sourceItems): void
37+
{
38+
$updatedSkus = [];
39+
foreach ($sourceItems as $sourceItem) {
40+
if ($sourceItem->hasDataChanges()) {
41+
$updatedSkus[] = $sourceItem->getSku();
42+
}
43+
}
44+
$this->scheduleProductUpdate->execute($updatedSkus);
45+
}
46+
}

0 commit comments

Comments
 (0)