Skip to content

Commit 5c5c339

Browse files
author
Mike Weis
committed
MAGETWO-28252: Catalog Inventory Integration API
- fixed
1 parent cc6ec2b commit 5c5c339

File tree

7 files changed

+762
-0
lines changed

7 files changed

+762
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
/**
3+
*
4+
* Copyright © 2015 Magento. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
8+
namespace Magento\CatalogInventory\Model\Plugin;
9+
10+
class AfterProductLoad
11+
{
12+
/**
13+
* @var \Magento\CatalogInventory\Api\StockRegistryInterface
14+
*/
15+
protected $stockRegistry;
16+
17+
/**
18+
* @var \Magento\Catalog\Api\Data\ProductExtensionFactory
19+
*/
20+
protected $productExtensionFactory;
21+
22+
/**
23+
* @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry
24+
* @param \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory
25+
*/
26+
public function __construct(
27+
\Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
28+
\Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory
29+
) {
30+
$this->stockRegistry = $stockRegistry;
31+
$this->productExtensionFactory = $productExtensionFactory;
32+
}
33+
34+
/**
35+
* Add stock item information to the product's extension attributes
36+
*
37+
* @param \Magento\Catalog\Model\Product $product
38+
* @return \Magento\Catalog\Model\Product
39+
*/
40+
public function afterLoad($product)
41+
{
42+
$productExtension = $product->getExtensionAttributes();
43+
if ($productExtension === null) {
44+
$productExtension = $this->productExtensionFactory->create();
45+
}
46+
// stockItem := \Magento\CatalogInventory\Api\Data\StockItemInterface
47+
$productExtension->setStockItem($this->stockRegistry->getStockItem($product->getId()));
48+
$product->setExtensionAttributes($productExtension);
49+
return $product;
50+
}
51+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
/**
3+
*
4+
* Copyright © 2015 Magento. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
8+
namespace Magento\CatalogInventory\Model\Plugin;
9+
10+
class AroundProductRepositorySave
11+
{
12+
/**
13+
* @var \Magento\CatalogInventory\Api\StockRegistryInterface
14+
*/
15+
protected $stockRegistry;
16+
17+
/**
18+
* @var \Magento\Store\Model\StoreManagerInterface
19+
*/
20+
protected $storeManager;
21+
22+
/**
23+
* @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry
24+
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
25+
*/
26+
public function __construct(
27+
\Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
28+
\Magento\Store\Model\StoreManagerInterface $storeManager
29+
) {
30+
$this->stockRegistry = $stockRegistry;
31+
$this->storeManager = $storeManager;
32+
}
33+
34+
/**
35+
* @param \Magento\Catalog\Api\ProductRepositoryInterface $subject
36+
* @param callable $proceed
37+
* @param \Magento\Catalog\Api\Data\ProductInterface $product
38+
* @param bool $saveOptions
39+
* @return \Magento\Catalog\Api\Data\ProductInterface
40+
* @throws \Magento\Framework\Exception\CouldNotSaveException
41+
*/
42+
public function aroundSave(
43+
\Magento\Catalog\Api\ProductRepositoryInterface $subject,
44+
\Closure $proceed,
45+
\Magento\Catalog\Api\Data\ProductInterface $product,
46+
$saveOptions = false
47+
) {
48+
/** @var \Magento\Catalog\Api\Data\ProductInterface $result */
49+
$result = $proceed($product, $saveOptions);
50+
51+
// all the data we care about will exist as extension attributes of the original product
52+
$extendedAttributes = $product->getExtensionAttributes();
53+
if ($extendedAttributes === null) {
54+
return $result;
55+
}
56+
57+
/* @var \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem */
58+
$stockItem = $extendedAttributes->getStockItem();
59+
if ($stockItem == null) {
60+
return $result;
61+
}
62+
63+
// set fields that the customer should not care about
64+
$stockItem->setProductId($result->getId());
65+
$stockItem->setWebsiteId($this->storeManager->getStore($result->getStoreId())->getWebsiteId());
66+
67+
// TODO: might need to handle a *new* -v- *update* for the stockItem
68+
// ... StockRegistry: $this->stockItemRepository->save
69+
// TODO: ensure this is correct logic for PUT/update and POST/create
70+
71+
$this->stockRegistry->updateStockItemBySku($result->getSku(), $stockItem);
72+
73+
// since we just saved a portion of the product, force a reload of it before returning it
74+
return $subject->get($result->getSku(), false, $result->getStoreId(), true);
75+
}
76+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
/**
3+
*
4+
* Copyright © 2015 Magento. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
8+
// @codingStandardsIgnoreFile
9+
10+
namespace Magento\CatalogInventory\Test\Unit\Model\Plugin;
11+
12+
class AfterProductLoadTest extends \PHPUnit_Framework_TestCase
13+
{
14+
/**
15+
* @var \Magento\CatalogInventory\Model\Plugin\AfterProductLoad
16+
*/
17+
protected $plugin;
18+
19+
/**
20+
* @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject
21+
*/
22+
protected $productMock;
23+
24+
/**
25+
* @var \Magento\Catalog\Api\Data\ProductExtensionFactory|\PHPUnit_Framework_MockObject_MockObject
26+
*/
27+
protected $productExtensionFactoryMock;
28+
29+
/**
30+
* @var \Magento\Catalog\Api\Data\ProductExtension|\PHPUnit_Framework_MockObject_MockObject
31+
*/
32+
protected $productExtensionMock;
33+
34+
protected function setUp()
35+
{
36+
$stockRegistryMock = $this->getMock('\Magento\CatalogInventory\Api\StockRegistryInterface');
37+
$this->productExtensionFactoryMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtensionFactory')
38+
->disableOriginalConstructor()
39+
->getMock();
40+
41+
$this->plugin = new \Magento\CatalogInventory\Model\Plugin\AfterProductLoad(
42+
$stockRegistryMock,
43+
$this->productExtensionFactoryMock
44+
);
45+
46+
$productId = 5494;
47+
$stockItemMock = $this->getMock('\Magento\CatalogInventory\Api\Data\StockItemInterface');
48+
49+
$stockRegistryMock->expects($this->once())
50+
->method('getStockItem')
51+
->with($productId)
52+
->willReturn($stockItemMock);
53+
54+
$this->productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
55+
->setMethods(['setStockItem'])
56+
->getMock();
57+
$this->productExtensionMock->expects($this->once())
58+
->method('setStockItem')
59+
->with($stockItemMock)
60+
->willReturnSelf();
61+
62+
$this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
63+
$this->productMock->expects($this->once())
64+
->method('setExtensionAttributes')
65+
->with($this->productExtensionMock)
66+
->willReturnSelf();
67+
$this->productMock->expects(($this->once()))
68+
->method('getId')
69+
->will($this->returnValue($productId));
70+
}
71+
72+
public function testAfterLoad()
73+
{
74+
// test when extension attributes are not (yet) present in the product
75+
$this->productMock->expects($this->once())
76+
->method('getExtensionAttributes')
77+
->willReturn(null);
78+
$this->productExtensionFactoryMock->expects($this->once())
79+
->method('create')
80+
->willReturn($this->productExtensionMock);
81+
82+
$this->assertEquals(
83+
$this->productMock,
84+
$this->plugin->afterLoad($this->productMock)
85+
);
86+
}
87+
88+
public function testAfterLoadWithExistingExtensionAttributes()
89+
{
90+
// test when extension attributes already exist
91+
$this->productMock->expects($this->once())
92+
->method('getExtensionAttributes')
93+
->willReturn($this->productExtensionMock);
94+
$this->productExtensionFactoryMock->expects($this->never())
95+
->method('create');
96+
97+
$this->assertEquals(
98+
$this->productMock,
99+
$this->plugin->afterLoad($this->productMock)
100+
);
101+
}
102+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?php
2+
/**
3+
*
4+
* Copyright © 2015 Magento. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
8+
// @codingStandardsIgnoreFile
9+
10+
namespace Magento\CatalogInventory\Test\Unit\Model\Plugin;
11+
12+
class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
13+
{
14+
/**
15+
* @var \Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave
16+
*/
17+
protected $plugin;
18+
19+
/**
20+
* @var \Magento\CatalogInventory\Api\Data\StockItemInterface|\PHPUnit_Framework_MockObject_MockObject
21+
*/
22+
protected $stockItemMock;
23+
24+
/**
25+
* @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject
26+
*/
27+
protected $productMock;
28+
29+
/**
30+
* @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject
31+
*/
32+
protected $savedProductMock;
33+
34+
/**
35+
* @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject
36+
*/
37+
protected $stockRegistry;
38+
39+
/**
40+
* @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
41+
*/
42+
protected $storeManager;
43+
44+
/**
45+
* @var \Magento\Catalog\Api\Data\ProductExtension|\PHPUnit_Framework_MockObject_MockObject
46+
*/
47+
protected $productExtensionMock;
48+
49+
/**
50+
* @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
51+
*/
52+
protected $productRepositoryMock;
53+
54+
/**
55+
* @var \Closure
56+
*/
57+
protected $closureMock;
58+
59+
public function setUp()
60+
{
61+
$this->stockRegistry = $this->getMock('\Magento\CatalogInventory\Api\StockRegistryInterface');
62+
$this->storeManager = $this->getMock('\Magento\Store\Model\StoreManagerInterface');
63+
64+
$this->plugin = new \Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave(
65+
$this->stockRegistry,
66+
$this->storeManager
67+
);
68+
69+
$this->productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
70+
->setMethods(['getStockItem'])
71+
->getMock();
72+
$this->productRepositoryMock = $this->getMock('Magento\Catalog\Api\ProductRepositoryInterface');
73+
$this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
74+
$this->savedProductMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
75+
$this->closureMock = function () {
76+
return $this->savedProductMock;
77+
};
78+
$this->stockItemMock = $this->getMock('\Magento\CatalogInventory\Api\Data\StockItemInterface');
79+
}
80+
81+
public function testAroundSaveWhenProductHasNoExtensionAttributes()
82+
{
83+
$this->productMock->expects($this->once())
84+
->method('getExtensionAttributes')
85+
->willReturn(null);
86+
$this->productExtensionMock->expects($this->never())->method('getStockItem');
87+
88+
$this->assertEquals(
89+
$this->savedProductMock,
90+
$this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
91+
);
92+
}
93+
94+
public function testAroundSaveWhenProductHasNoStockItemAttributes()
95+
{
96+
$this->productMock->expects($this->once())
97+
->method('getExtensionAttributes')
98+
->willReturn($this->productExtensionMock);
99+
$this->productExtensionMock->expects($this->once())
100+
->method('getStockItem')
101+
->willReturn(null);
102+
$this->stockItemMock->expects($this->never())->method('setProductId');
103+
$this->stockItemMock->expects($this->never())->method('setWebsiteId');
104+
105+
$this->assertEquals(
106+
$this->savedProductMock,
107+
$this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
108+
);
109+
}
110+
111+
public function testAroundSave()
112+
{
113+
$productId = 5494;
114+
$websiteId = 1;
115+
$storeId = 2;
116+
$sku = 'my product that needs saving';
117+
118+
$this->productMock->expects($this->once())
119+
->method('getExtensionAttributes')
120+
->willReturn($this->productExtensionMock);
121+
$this->productExtensionMock->expects($this->once())
122+
->method('getStockItem')
123+
->willReturn($this->stockItemMock);
124+
125+
$storeMock = $this->getMockBuilder('\Magento\Store\Model\Store')
126+
->disableOriginalConstructor()->getMock();
127+
$storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId);
128+
$this->storeManager->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock);
129+
130+
$this->savedProductMock->expects(($this->once()))->method('getId')->willReturn($productId);
131+
$this->savedProductMock->expects(($this->atLeastOnce()))->method('getStoreId')->willReturn($storeId);
132+
$this->savedProductMock->expects($this->atLeastOnce())->method('getSku')->willReturn($sku);
133+
134+
$this->stockItemMock->expects($this->once())->method('setProductId')->with($productId);
135+
$this->stockItemMock->expects($this->once())->method('setWebsiteId')->with($websiteId);
136+
137+
$this->stockRegistry->expects($this->once())
138+
->method('updateStockItemBySku')
139+
->with($sku, $this->stockItemMock);
140+
141+
$newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface')
142+
->disableOriginalConstructor()->getMock();
143+
$this->productRepositoryMock->expects($this->once())
144+
->method('get')
145+
->with($sku, false, $storeId, true)
146+
->willReturn($newProductMock);
147+
148+
$this->assertEquals(
149+
$newProductMock,
150+
$this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
151+
);
152+
}
153+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
/**
4+
* Copyright © 2015 Magento. 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="../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
9+
<custom_attributes for="Magento\Catalog\Api\Data\ProductInterface">
10+
<attribute code="stock_item" type="Magento\CatalogInventory\Api\Data\StockItemInterface" />
11+
</custom_attributes>
12+
</config>

0 commit comments

Comments
 (0)