Skip to content

Commit 776515a

Browse files
authored
Merge branch '2.4-develop' into MC-38105
2 parents 667e1dc + a284b63 commit 776515a

File tree

11 files changed

+457
-99
lines changed

11 files changed

+457
-99
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogInventory\Model;
9+
10+
use Magento\CatalogInventory\Api\StockConfigurationInterface;
11+
use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
12+
use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory;
13+
use Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory;
14+
use Magento\CatalogInventory\Api\StockStatusRepositoryInterface;
15+
16+
/**
17+
* Preload stock data into stock registry
18+
*/
19+
class StockRegistryPreloader
20+
{
21+
/**
22+
* @var StockItemRepositoryInterface
23+
*/
24+
private $stockItemRepository;
25+
/**
26+
* @var StockConfigurationInterface
27+
*/
28+
private $stockConfiguration;
29+
/**
30+
* @var StockRegistryStorage
31+
*/
32+
private $stockRegistryStorage;
33+
/**
34+
* @var StockItemCriteriaInterfaceFactory
35+
*/
36+
private $stockItemCriteriaFactory;
37+
/**
38+
* @var StockStatusCriteriaInterfaceFactory
39+
*/
40+
private $stockStatusCriteriaFactory;
41+
/**
42+
* @var StockStatusRepositoryInterface
43+
*/
44+
private $stockStatusRepository;
45+
46+
/**
47+
* @param StockItemRepositoryInterface $stockItemRepository
48+
* @param StockStatusRepositoryInterface $stockStatusRepository
49+
* @param StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory
50+
* @param StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory
51+
* @param StockConfigurationInterface $stockConfiguration
52+
* @param StockRegistryStorage $stockRegistryStorage
53+
*/
54+
public function __construct(
55+
StockItemRepositoryInterface $stockItemRepository,
56+
StockStatusRepositoryInterface $stockStatusRepository,
57+
StockItemCriteriaInterfaceFactory $stockItemCriteriaFactory,
58+
StockStatusCriteriaInterfaceFactory $stockStatusCriteriaFactory,
59+
StockConfigurationInterface $stockConfiguration,
60+
StockRegistryStorage $stockRegistryStorage
61+
) {
62+
$this->stockItemRepository = $stockItemRepository;
63+
$this->stockStatusRepository = $stockStatusRepository;
64+
$this->stockItemCriteriaFactory = $stockItemCriteriaFactory;
65+
$this->stockStatusCriteriaFactory = $stockStatusCriteriaFactory;
66+
$this->stockConfiguration = $stockConfiguration;
67+
$this->stockRegistryStorage = $stockRegistryStorage;
68+
}
69+
70+
/**
71+
* Preload stock item into stock registry
72+
*
73+
* @param array $productIds
74+
* @param int|null $scopeId
75+
* @return \Magento\CatalogInventory\Api\Data\StockItemInterface[]
76+
*/
77+
public function preloadStockItems(array $productIds, ?int $scopeId = null): array
78+
{
79+
$scopeId = $scopeId ?? $this->stockConfiguration->getDefaultScopeId();
80+
$criteria = $this->stockItemCriteriaFactory->create();
81+
$criteria->setProductsFilter($productIds);
82+
$criteria->setScopeFilter($scopeId);
83+
$collection = $this->stockItemRepository->getList($criteria);
84+
$this->setStockItems($collection->getItems(), $scopeId);
85+
return $collection->getItems();
86+
}
87+
88+
/**
89+
* Saves stock items into registry
90+
*
91+
* @param \Magento\CatalogInventory\Api\Data\StockItemInterface[] $stockItems
92+
* @param int $scopeId
93+
*/
94+
public function setStockItems(array $stockItems, int $scopeId): void
95+
{
96+
foreach ($stockItems as $item) {
97+
$this->stockRegistryStorage->setStockItem($item->getProductId(), $scopeId, $item);
98+
}
99+
}
100+
101+
/**
102+
* Preload stock status into stock registry
103+
*
104+
* @param array $productIds
105+
* @param int|null $scopeId
106+
* @return \Magento\CatalogInventory\Api\Data\StockStatusInterface[]
107+
*/
108+
public function preloadStockStatuses(array $productIds, ?int $scopeId = null): array
109+
{
110+
$scopeId = $scopeId ?? $this->stockConfiguration->getDefaultScopeId();
111+
$criteria = $this->stockStatusCriteriaFactory->create();
112+
$criteria->setProductsFilter($productIds);
113+
$criteria->setScopeFilter($scopeId);
114+
$collection = $this->stockStatusRepository->getList($criteria);
115+
$this->setStockStatuses($collection->getItems(), $scopeId);
116+
return $collection->getItems();
117+
}
118+
119+
/**
120+
* Saves stock statuses into registry
121+
*
122+
* @param \Magento\CatalogInventory\Api\Data\StockStatusInterface[] $stockStatuses
123+
* @param int $scopeId
124+
*/
125+
public function setStockStatuses(array $stockStatuses, int $scopeId): void
126+
{
127+
foreach ($stockStatuses as $item) {
128+
$this->stockRegistryStorage->setStockStatus($item->getProductId(), $scopeId, $item);
129+
}
130+
}
131+
}

app/code/Magento/CatalogInventory/Observer/AddStockItemsObserver.php

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
use Magento\Catalog\Model\ResourceModel\Product\Collection;
1111
use Magento\CatalogInventory\Api\StockConfigurationInterface;
1212
use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory;
13-
use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
13+
use Magento\CatalogInventory\Model\StockRegistryPreloader;
1414
use Magento\Framework\Event\Observer;
1515
use Magento\Framework\Event\ObserverInterface;
1616

@@ -19,36 +19,27 @@
1919
*/
2020
class AddStockItemsObserver implements ObserverInterface
2121
{
22-
/**
23-
* @var StockItemCriteriaInterfaceFactory
24-
*/
25-
private $criteriaInterfaceFactory;
26-
27-
/**
28-
* @var StockItemRepositoryInterface
29-
*/
30-
private $stockItemRepository;
31-
3222
/**
3323
* @var StockConfigurationInterface
3424
*/
3525
private $stockConfiguration;
26+
/**
27+
* @var StockRegistryPreloader
28+
*/
29+
private $stockRegistryPreloader;
3630

3731
/**
3832
* AddStockItemsObserver constructor.
3933
*
40-
* @param StockItemCriteriaInterfaceFactory $criteriaInterfaceFactory
41-
* @param StockItemRepositoryInterface $stockItemRepository
4234
* @param StockConfigurationInterface $stockConfiguration
35+
* @param StockRegistryPreloader $stockRegistryPreloader
4336
*/
4437
public function __construct(
45-
StockItemCriteriaInterfaceFactory $criteriaInterfaceFactory,
46-
StockItemRepositoryInterface $stockItemRepository,
47-
StockConfigurationInterface $stockConfiguration
38+
StockConfigurationInterface $stockConfiguration,
39+
StockRegistryPreloader $stockRegistryPreloader
4840
) {
49-
$this->criteriaInterfaceFactory = $criteriaInterfaceFactory;
50-
$this->stockItemRepository = $stockItemRepository;
5141
$this->stockConfiguration = $stockConfiguration;
42+
$this->stockRegistryPreloader = $stockRegistryPreloader;
5243
}
5344

5445
/**
@@ -62,11 +53,13 @@ public function execute(Observer $observer)
6253
/** @var Collection $productCollection */
6354
$productCollection = $observer->getData('collection');
6455
$productIds = array_keys($productCollection->getItems());
65-
$criteria = $this->criteriaInterfaceFactory->create();
66-
$criteria->setProductsFilter($productIds);
67-
$criteria->setScopeFilter($this->stockConfiguration->getDefaultScopeId());
68-
$stockItemCollection = $this->stockItemRepository->getList($criteria);
69-
foreach ($stockItemCollection->getItems() as $item) {
56+
$scopeId = $this->stockConfiguration->getDefaultScopeId();
57+
$stockItems = [];
58+
if ($productIds) {
59+
$stockItems = $this->stockRegistryPreloader->preloadStockItems($productIds, $scopeId);
60+
$this->stockRegistryPreloader->preloadStockStatuses($productIds, $scopeId);
61+
}
62+
foreach ($stockItems as $item) {
7063
/** @var Product $product */
7164
$product = $productCollection->getItemById($item->getProductId());
7265
$productExtension = $product->getExtensionAttributes();
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogInventory\Test\Unit\Model;
9+
10+
use Magento\CatalogInventory\Api\Data\StockItemCollectionInterface;
11+
use Magento\CatalogInventory\Api\Data\StockItemInterface;
12+
use Magento\CatalogInventory\Api\Data\StockStatusCollectionInterface;
13+
use Magento\CatalogInventory\Api\Data\StockStatusInterface;
14+
use Magento\CatalogInventory\Api\StockConfigurationInterface;
15+
use Magento\CatalogInventory\Api\StockItemCriteriaInterface;
16+
use Magento\CatalogInventory\Api\StockItemRepositoryInterface;
17+
use Magento\CatalogInventory\Api\StockItemCriteriaInterfaceFactory;
18+
use Magento\CatalogInventory\Api\StockStatusCriteriaInterface;
19+
use Magento\CatalogInventory\Api\StockStatusCriteriaInterfaceFactory;
20+
use Magento\CatalogInventory\Api\StockStatusRepositoryInterface;
21+
use Magento\CatalogInventory\Model\StockRegistryPreloader;
22+
use Magento\CatalogInventory\Model\StockRegistryStorage;
23+
use PHPUnit\Framework\MockObject\MockObject;
24+
use PHPUnit\Framework\TestCase;
25+
26+
/**
27+
* Test for StockRegistryStorage
28+
*
29+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
30+
*/
31+
class StockRegistryPreloaderTest extends TestCase
32+
{
33+
/**
34+
* @var StockItemRepositoryInterface|MockObject
35+
*/
36+
private $stockItemRepository;
37+
/**
38+
* @var StockStatusRepositoryInterface|MockObject
39+
*/
40+
private $stockStatusRepository;
41+
/**
42+
* @var MockObject
43+
*/
44+
private $stockItemCriteriaFactory;
45+
/**
46+
* @var MockObject
47+
*/
48+
private $stockStatusCriteriaFactory;
49+
/**
50+
* @var StockConfigurationInterface|MockObject
51+
*/
52+
private $stockConfiguration;
53+
/**
54+
* @var StockRegistryStorage
55+
*/
56+
private $stockRegistryStorage;
57+
/**
58+
* @var StockRegistryPreloader
59+
*/
60+
private $model;
61+
62+
/**
63+
* @inheritDoc
64+
*/
65+
protected function setUp(): void
66+
{
67+
parent::setUp();
68+
$this->stockItemRepository = $this->createMock(StockItemRepositoryInterface::class);
69+
$this->stockStatusRepository = $this->createMock(StockStatusRepositoryInterface::class);
70+
$this->stockItemCriteriaFactory = $this->createMock(StockItemCriteriaInterfaceFactory::class);
71+
$this->stockStatusCriteriaFactory = $this->createMock(StockStatusCriteriaInterfaceFactory::class);
72+
$this->stockConfiguration = $this->createMock(StockConfigurationInterface::class);
73+
$this->stockRegistryStorage = new StockRegistryStorage();
74+
$this->model = new StockRegistryPreloader(
75+
$this->stockItemRepository,
76+
$this->stockStatusRepository,
77+
$this->stockItemCriteriaFactory,
78+
$this->stockStatusCriteriaFactory,
79+
$this->stockConfiguration,
80+
$this->stockRegistryStorage,
81+
);
82+
}
83+
84+
public function testPreloadStockItems(): void
85+
{
86+
$productIds = [10, 20];
87+
$scopeId = 1;
88+
$stockItems = [
89+
$this->createConfiguredMock(StockItemInterface::class, ['getProductId' => 10]),
90+
$this->createConfiguredMock(StockItemInterface::class, ['getProductId' => 20]),
91+
];
92+
$collection = $this->createConfiguredMock(StockItemCollectionInterface::class, ['getItems' => $stockItems]);
93+
$criteria = $this->createMock(StockItemCriteriaInterface::class);
94+
$criteria->expects($this->once())
95+
->method('setProductsFilter')
96+
->with($productIds)
97+
->willReturnSelf();
98+
$criteria->expects($this->once())
99+
->method('setScopeFilter')
100+
->with($scopeId)
101+
->willReturnSelf();
102+
$this->stockItemRepository->method('getList')
103+
->willReturn($collection);
104+
$this->stockItemCriteriaFactory->method('create')
105+
->willReturn($criteria);
106+
$this->assertEquals($stockItems, $this->model->preloadStockItems($productIds, $scopeId));
107+
$this->assertSame($stockItems[0], $this->stockRegistryStorage->getStockItem(10, $scopeId));
108+
$this->assertSame($stockItems[1], $this->stockRegistryStorage->getStockItem(20, $scopeId));
109+
}
110+
111+
public function testPreloadStockStatuses(): void
112+
{
113+
$productIds = [10, 20];
114+
$scopeId = 1;
115+
$stockItems = [
116+
$this->createConfiguredMock(StockStatusInterface::class, ['getProductId' => 10]),
117+
$this->createConfiguredMock(StockStatusInterface::class, ['getProductId' => 20]),
118+
];
119+
$collection = $this->createConfiguredMock(StockStatusCollectionInterface::class, ['getItems' => $stockItems]);
120+
$criteria = $this->createMock(StockStatusCriteriaInterface::class);
121+
$criteria->expects($this->once())
122+
->method('setProductsFilter')
123+
->with($productIds)
124+
->willReturnSelf();
125+
$criteria->expects($this->once())
126+
->method('setScopeFilter')
127+
->with($scopeId)
128+
->willReturnSelf();
129+
$this->stockStatusRepository->method('getList')
130+
->willReturn($collection);
131+
$this->stockStatusCriteriaFactory->method('create')
132+
->willReturn($criteria);
133+
$this->assertEquals($stockItems, $this->model->preloadStockStatuses($productIds, $scopeId));
134+
$this->assertSame($stockItems[0], $this->stockRegistryStorage->getStockStatus(10, $scopeId));
135+
$this->assertSame($stockItems[1], $this->stockRegistryStorage->getStockStatus(20, $scopeId));
136+
}
137+
138+
public function testSetStockItems(): void
139+
{
140+
$scopeId = 1;
141+
$stockItems = [
142+
$this->createConfiguredMock(StockItemInterface::class, ['getProductId' => 10]),
143+
$this->createConfiguredMock(StockItemInterface::class, ['getProductId' => 20]),
144+
];
145+
$this->model->setStockItems($stockItems, $scopeId);
146+
$this->assertSame($stockItems[0], $this->stockRegistryStorage->getStockItem(10, $scopeId));
147+
$this->assertSame($stockItems[1], $this->stockRegistryStorage->getStockItem(20, $scopeId));
148+
}
149+
150+
public function testSetStockStatuses(): void
151+
{
152+
$scopeId = 1;
153+
$stockItems = [
154+
$this->createConfiguredMock(StockStatusInterface::class, ['getProductId' => 10]),
155+
$this->createConfiguredMock(StockStatusInterface::class, ['getProductId' => 20]),
156+
];
157+
$this->model->setStockStatuses($stockItems, $scopeId);
158+
$this->assertSame($stockItems[0], $this->stockRegistryStorage->getStockStatus(10, $scopeId));
159+
$this->assertSame($stockItems[1], $this->stockRegistryStorage->getStockStatus(20, $scopeId));
160+
}
161+
}

0 commit comments

Comments
 (0)