Skip to content

Commit dfd934f

Browse files
author
Andrey Konosov
committed
Merge commit 'refs/pull/1566/head' of github.com:magento/magento2ce into 2.2-develop
2 parents 30a16fa + 4ed54d7 commit dfd934f

File tree

9 files changed

+414
-14
lines changed

9 files changed

+414
-14
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\ConfigurableProduct\Model\ResourceModel\Product;
7+
8+
use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
9+
use Magento\Catalog\Model\ResourceModel\Product\LinkedProductSelectBuilderInterface;
10+
11+
/**
12+
* A decorator for a linked product select builder.
13+
*
14+
* Extends functionality of the linked product select builder to allow perform
15+
* some additional processing of built Select objects.
16+
*/
17+
class LinkedProductSelectBuilder implements LinkedProductSelectBuilderInterface
18+
{
19+
/**
20+
* @var BaseSelectProcessorInterface
21+
*/
22+
private $baseSelectProcessor;
23+
24+
/**
25+
* @var LinkedProductSelectBuilderInterface
26+
*/
27+
private $linkedProductSelectBuilder;
28+
29+
/**
30+
* @param BaseSelectProcessorInterface $baseSelectProcessor
31+
* @param LinkedProductSelectBuilderInterface $linkedProductSelectBuilder
32+
*/
33+
public function __construct(
34+
BaseSelectProcessorInterface $baseSelectProcessor,
35+
LinkedProductSelectBuilderInterface $linkedProductSelectBuilder
36+
) {
37+
$this->baseSelectProcessor = $baseSelectProcessor;
38+
$this->linkedProductSelectBuilder = $linkedProductSelectBuilder;
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function build($productId)
45+
{
46+
$selects = $this->linkedProductSelectBuilder->build($productId);
47+
48+
foreach ($selects as $select) {
49+
$this->baseSelectProcessor->process($select);
50+
}
51+
52+
return $selects;
53+
}
54+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\ConfigurableProduct\Model\ResourceModel\Product;
7+
8+
use Magento\Framework\DB\Select;
9+
use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
10+
use Magento\CatalogInventory\Api\StockConfigurationInterface;
11+
use Magento\CatalogInventory\Model\Stock\Status as StockStatus;
12+
use Magento\CatalogInventory\Model\ResourceModel\Stock\Status as StockStatusResource;
13+
14+
/**
15+
* A Select object processor.
16+
*
17+
* Adds stock status limitations to a given Select object.
18+
*/
19+
class StockStatusBaseSelectProcessor implements BaseSelectProcessorInterface
20+
{
21+
/**
22+
* @var StockConfigurationInterface
23+
*/
24+
private $stockConfig;
25+
26+
/**
27+
* @var StockStatusResource
28+
*/
29+
private $stockStatusResource;
30+
31+
/**
32+
* @param StockConfigurationInterface $stockConfig
33+
* @param StockStatusResource $stockStatusResource
34+
*/
35+
public function __construct(
36+
StockConfigurationInterface $stockConfig,
37+
StockStatusResource $stockStatusResource
38+
) {
39+
$this->stockConfig = $stockConfig;
40+
$this->stockStatusResource = $stockStatusResource;
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function process(Select $select)
47+
{
48+
if ($this->stockConfig->isShowOutOfStock()) {
49+
$select->joinInner(
50+
['stock' => $this->stockStatusResource->getMainTable()],
51+
sprintf(
52+
'stock.product_id = %s.entity_id',
53+
BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS
54+
),
55+
[]
56+
)->where(
57+
'stock.stock_status = ?',
58+
StockStatus::STATUS_IN_STOCK
59+
);
60+
}
61+
62+
return $select;
63+
}
64+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\ConfigurableProduct\Plugin\Catalog\Model\Product\Pricing\Renderer;
7+
8+
use Magento\ConfigurableProduct\Pricing\Price\LowestPriceOptionsProviderInterface;
9+
10+
/**
11+
* A plugin for a salable resolver.
12+
*/
13+
class SalableResolver
14+
{
15+
/**
16+
* @var LowestPriceOptionsProviderInterface
17+
*/
18+
private $lowestPriceOptionsProvider;
19+
20+
/**
21+
* @param LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider
22+
*/
23+
public function __construct(
24+
LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider
25+
) {
26+
$this->lowestPriceOptionsProvider = $lowestPriceOptionsProvider;
27+
}
28+
29+
/**
30+
* Performs an additional check whether given configurable product has
31+
* at least one configuration in-stock.
32+
*
33+
* @param \Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver $subject
34+
* @param bool $result
35+
* @param \Magento\Framework\Pricing\SaleableInterface $salableItem
36+
*
37+
* @return bool
38+
*
39+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
40+
*/
41+
public function afterIsSalable(
42+
\Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver $subject,
43+
$result,
44+
\Magento\Framework\Pricing\SaleableInterface $salableItem
45+
) {
46+
if ($salableItem->getTypeId() == 'configurable' && $result) {
47+
if (!$this->lowestPriceOptionsProvider->getProducts($salableItem)) {
48+
$result = false;
49+
}
50+
}
51+
52+
return $result;
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\ConfigurableProduct\Test\Unit\Model\ResourceModel\Product;
7+
8+
use Magento\Framework\DB\Select;
9+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
10+
use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
11+
use Magento\Catalog\Model\ResourceModel\Product\LinkedProductSelectBuilderInterface;
12+
use Magento\ConfigurableProduct\Model\ResourceModel\Product\LinkedProductSelectBuilder;
13+
14+
class LinkedProductSelectBuilderTest extends \PHPUnit\Framework\TestCase
15+
{
16+
/**
17+
* @var LinkedProductSelectBuilder
18+
*/
19+
private $subject;
20+
21+
/**
22+
* @var BaseSelectProcessorInterface|\PHPUnit_Framework_MockObject_MockObject
23+
*/
24+
private $baseSelectProcessorMock;
25+
26+
/**
27+
* @var LinkedProductSelectBuilderInterface|\PHPUnit_Framework_MockObject_MockObject
28+
*/
29+
private $linkedProductSelectBuilderMock;
30+
31+
protected function setUp()
32+
{
33+
$this->baseSelectProcessorMock = $this->getMockBuilder(BaseSelectProcessorInterface::class)
34+
->disableOriginalConstructor()
35+
->getMockForAbstractClass();
36+
37+
$this->linkedProductSelectBuilderMock = $this->getMockBuilder(LinkedProductSelectBuilderInterface::class)
38+
->disableOriginalConstructor()
39+
->getMockForAbstractClass();
40+
41+
$this->subject = (new ObjectManager($this))->getObject(
42+
LinkedProductSelectBuilder::class,
43+
[
44+
'baseSelectProcessor' => $this->baseSelectProcessorMock,
45+
'linkedProductSelectBuilder' => $this->linkedProductSelectBuilderMock
46+
]
47+
);
48+
}
49+
50+
public function testBuild()
51+
{
52+
$productId = 42;
53+
54+
/** @var Select|\PHPUnit_Framework_MockObject_MockObject $selectMock */
55+
$selectMock = $this->getMockBuilder(Select::class)
56+
->disableOriginalConstructor()
57+
->getMock();
58+
59+
$expectedResult = [$selectMock];
60+
61+
$this->linkedProductSelectBuilderMock->expects($this->any())
62+
->method('build')
63+
->with($productId)
64+
->willReturn($expectedResult);
65+
66+
$this->baseSelectProcessorMock->expects($this->once())
67+
->method('process')
68+
->with($selectMock);
69+
70+
$this->assertEquals($expectedResult, $this->subject->build($productId));
71+
}
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\ConfigurableProduct\Test\Unit\Model\ResourceModel\Product;
7+
8+
use Magento\Framework\DB\Select;
9+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
10+
use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
11+
use Magento\CatalogInventory\Api\StockConfigurationInterface;
12+
use Magento\CatalogInventory\Model\Stock\Status as StockStatus;
13+
use Magento\CatalogInventory\Model\ResourceModel\Stock\Status as StockStatusResource;
14+
use Magento\ConfigurableProduct\Model\ResourceModel\Product\StockStatusBaseSelectProcessor;
15+
16+
class StockStatusBaseSelectProcessorTest extends \PHPUnit\Framework\TestCase
17+
{
18+
/**
19+
* @var StockStatusBaseSelectProcessor
20+
*/
21+
private $subject;
22+
23+
/**
24+
* @var StockConfigurationInterface|\PHPUnit_Framework_MockObject_MockObject
25+
*/
26+
private $stockConfigMock;
27+
28+
/**
29+
* @var string
30+
*/
31+
private $stockStatusTable = 'cataloginventory_stock_status';
32+
33+
/**
34+
* @var StockStatusResource|\PHPUnit_Framework_MockObject_MockObject
35+
*/
36+
private $stockStatusResourceMock;
37+
38+
protected function setUp()
39+
{
40+
$this->stockConfigMock = $this->getMockBuilder(StockConfigurationInterface::class)
41+
->disableOriginalConstructor()
42+
->getMockForAbstractClass();
43+
44+
$this->stockStatusResourceMock = $this->getMockBuilder(StockStatusResource::class)
45+
->disableOriginalConstructor()
46+
->getMock();
47+
$this->stockStatusResourceMock->expects($this->any())
48+
->method('getMainTable')
49+
->willReturn($this->stockStatusTable);
50+
51+
$this->subject = (new ObjectManager($this))->getObject(
52+
StockStatusBaseSelectProcessor::class,
53+
[
54+
'stockConfig' => $this->stockConfigMock,
55+
'stockStatusResource' => $this->stockStatusResourceMock
56+
]
57+
);
58+
}
59+
60+
/**
61+
* @param bool $isShowOutOfStock
62+
*
63+
* @dataProvider processDataProvider
64+
*/
65+
public function testProcess($isShowOutOfStock)
66+
{
67+
$this->stockConfigMock->expects($this->any())
68+
->method('isShowOutOfStock')
69+
->willReturn($isShowOutOfStock);
70+
71+
/** @var Select|\PHPUnit_Framework_MockObject_MockObject $selectMock */
72+
$selectMock = $this->getMockBuilder(Select::class)
73+
->disableOriginalConstructor()
74+
->getMock();
75+
76+
if ($isShowOutOfStock) {
77+
$selectMock->expects($this->once())
78+
->method('joinInner')
79+
->with(
80+
['stock' => $this->stockStatusTable],
81+
sprintf(
82+
'stock.product_id = %s.entity_id',
83+
BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS
84+
),
85+
[]
86+
)
87+
->willReturnSelf();
88+
$selectMock->expects($this->once())
89+
->method('where')
90+
->with(
91+
'stock.stock_status = ?',
92+
StockStatus::STATUS_IN_STOCK
93+
)
94+
->willReturnSelf();
95+
} else {
96+
$selectMock->expects($this->never())
97+
->method($this->anything());
98+
}
99+
100+
$this->assertEquals($selectMock, $this->subject->process($selectMock));
101+
}
102+
103+
/**
104+
* @return array
105+
*/
106+
public function processDataProvider()
107+
{
108+
return [
109+
'Out of stock products are being displayed' => [true],
110+
'Out of stock products are NOT being displayed' => [false]
111+
];
112+
}
113+
}

app/code/Magento/ConfigurableProduct/etc/di.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,17 @@
196196
<argument name="productIndexer" xsi:type="object">Magento\Catalog\Model\Indexer\Product\Full</argument>
197197
</arguments>
198198
</type>
199+
<type name="Magento\ConfigurableProduct\Pricing\Price\LowestPriceOptionsProvider">
200+
<arguments>
201+
<argument name="linkedProductSelectBuilder" xsi:type="object">Magento\ConfigurableProduct\Model\ResourceModel\Product\LinkedProductSelectBuilder</argument>
202+
</arguments>
203+
</type>
204+
<type name="Magento\ConfigurableProduct\Model\ResourceModel\Product\LinkedProductSelectBuilder">
205+
<arguments>
206+
<argument name="baseSelectProcessor" xsi:type="object">Magento\ConfigurableProduct\Model\ResourceModel\Product\StockStatusBaseSelectProcessor</argument>
207+
</arguments>
208+
</type>
209+
<type name="Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver">
210+
<plugin name="configurable" type="Magento\ConfigurableProduct\Plugin\Catalog\Model\Product\Pricing\Renderer\SalableResolver" />
211+
</type>
199212
</config>

dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/CatalogProductSimple.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@
10961096
<item name="is_in_stock" xsi:type="string">Out of Stock</item>
10971097
</field>
10981098
<field name="price" xsi:type="array">
1099-
<item name="value" xsi:type="string">560</item>
1099+
<item name="value" xsi:type="string">561</item>
11001100
</field>
11011101
<field name="tax_class_id" xsi:type="array">
11021102
<item name="dataset" xsi:type="string">taxable_goods</item>

0 commit comments

Comments
 (0)