Skip to content

Commit

Permalink
MAGETWO-52717: [GitHub] Configurable product disabling lowest price a…
Browse files Browse the repository at this point in the history
…ssociated product still shows its price #4419

-- refactoring
-- test coverage
  • Loading branch information
vnayda committed Oct 5, 2016
1 parent 7c9547e commit eacec0e
Show file tree
Hide file tree
Showing 13 changed files with 494 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product;

use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
use Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor;
use Magento\Framework\DB\Select;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;

class CompositeBaseSelectProcessorTest extends \PHPUnit_Framework_TestCase
{
/**
* @var ObjectManager
*/
private $objectManager;

protected function setUp()
{
$this->objectManager = new ObjectManager($this);
}

/**
* @expectedException \Magento\Framework\Exception\InputException
*/
public function testInitializeWithWrongProcessorInstance()
{
$processorValid = $this->getMock(BaseSelectProcessorInterface::class);
$processorInvalid = $this->getMock(\stdClass::class);

$this->objectManager->getObject(CompositeBaseSelectProcessor::class, [
'baseSelectProcessors' => [$processorValid, $processorInvalid],
]);
}

public function testProcess()
{
$select = $this->getMockBuilder(Select::class)->disableOriginalConstructor()->getMock();

$processorFirst = $this->getMock(BaseSelectProcessorInterface::class);
$processorFirst->expects($this->once())->method('process')->with($select)->willReturn($select);

$processorSecond = $this->getMock(BaseSelectProcessorInterface::class);
$processorSecond->expects($this->once())->method('process')->with($select)->willReturn($select);

/** @var CompositeBaseSelectProcessor $baseSelectProcessors */
$baseSelectProcessors = $this->objectManager->getObject(CompositeBaseSelectProcessor::class, [
'baseSelectProcessors' => [$processorFirst, $processorSecond],
]);
$this->assertEquals($select, $baseSelectProcessors->process($select));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Product;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
use Magento\Catalog\Model\ResourceModel\Product\StatusBaseSelectProcessor;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\Entity\Attribute\AttributeInterface;
use Magento\Framework\DB\Select;
use Magento\Framework\EntityManager\EntityMetadataInterface;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;

class StatusBaseSelectProcessorTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Config|\PHPUnit_Framework_MockObject_MockObject
*/
private $eavConfig;

/**
* @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject
*/
private $metadataPool;

/**
* @var Select|\PHPUnit_Framework_MockObject_MockObject
*/
private $select;

/**
* @var StatusBaseSelectProcessor
*/
private $statusBaseSelectProcessor;

protected function setUp()
{
$this->eavConfig = $this->getMockBuilder(Config::class)->disableOriginalConstructor()->getMock();
$this->metadataPool = $this->getMockBuilder(MetadataPool::class)->disableOriginalConstructor()->getMock();
$this->select = $this->getMockBuilder(Select::class)->disableOriginalConstructor()->getMock();

$this->statusBaseSelectProcessor = (new ObjectManager($this))->getObject(StatusBaseSelectProcessor::class, [
'eavConfig' => $this->eavConfig,
'metadataPool' => $this->metadataPool,
]);
}

public function testProcess()
{
$linkField = 'link_field';
$backendTable = 'backend_table';
$attributeId = 'attribute_id';

$metadata = $this->getMock(EntityMetadataInterface::class);
$metadata->expects($this->once())
->method('getLinkField')
->willReturn($linkField);
$this->metadataPool->expects($this->once())
->method('getMetadata')
->with(ProductInterface::class)
->willReturn($metadata);

$statusAttribute = $this->getMockBuilder(AttributeInterface::class)
->setMethods(['getBackendTable', 'getAttributeId'])
->getMock();
$statusAttribute->expects($this->once())
->method('getBackendTable')
->willReturn($backendTable);
$statusAttribute->expects($this->once())
->method('getAttributeId')
->willReturn($attributeId);
$this->eavConfig->expects($this->once())
->method('getAttribute')
->with(Product::ENTITY, ProductInterface::STATUS)
->willReturn($statusAttribute);

$this->select->expects($this->once())
->method('join')
->with(
['status_attr' => $backendTable],
sprintf('status_attr.%s = %s.%1$s', $linkField, BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS),
[]
)
->willReturnSelf();
$this->select->expects($this->at(1))
->method('where')
->with('status_attr.attribute_id = ?', $attributeId)
->willReturnSelf();
$this->select->expects($this->at(2))
->method('where')
->with('status_attr.value = ?', Status::STATUS_ENABLED)
->willReturnSelf();

$this->assertEquals($this->select, $this->statusBaseSelectProcessor->process($this->select));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,6 @@ class ConfigurableOptionsProvider implements ConfigurableOptionsProviderInterfac
/** @var Configurable */
private $configurable;

/**
* @var RequestSafetyInterface
*/
private $requestSafety;

/**
* @var ResourceConnection
*/
private $resource;

/**
* @var LinkedProductSelectBuilderInterface
*/
private $linkedProductSelectBuilder;

/**
* @var CollectionFactory
*/
private $collectionFactory;

/**
* @var ProductInterface[]
*/
Expand All @@ -49,6 +29,7 @@ class ConfigurableOptionsProvider implements ConfigurableOptionsProviderInterfac
* @param LinkedProductSelectBuilderInterface $linkedProductSelectBuilder
* @param CollectionFactory $collectionFactory
* @param RequestSafetyInterface $requestSafety
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
Configurable $configurable,
Expand All @@ -58,10 +39,6 @@ public function __construct(
RequestSafetyInterface $requestSafety
) {
$this->configurable = $configurable;
$this->resource = $resourceConnection;
$this->linkedProductSelectBuilder = $linkedProductSelectBuilder;
$this->collectionFactory = $collectionFactory;
$this->requestSafety = $requestSafety;
}

/**
Expand All @@ -70,17 +47,7 @@ public function __construct(
public function getProducts(ProductInterface $product)
{
if (!isset($this->products[$product->getId()])) {
if ($this->requestSafety->isSafeMethod()) {
$productIds = $this->resource->getConnection()->fetchCol(
'(' . implode(') UNION (', $this->linkedProductSelectBuilder->build($product->getId())) . ')'
);

$this->products[$product->getId()] = $this->collectionFactory->create()
->addAttributeToSelect(['price', 'special_price'])
->addIdFilter($productIds);
} else {
$this->products[$product->getId()] = $this->configurable->getUsedProducts($product);
}
$this->products[$product->getId()] = $this->configurable->getUsedProducts($product);
}
return $this->products[$product->getId()];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

namespace Magento\ConfigurableProduct\Pricing\Price;

use Magento\Catalog\Model\Product;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Pricing\PriceCurrencyInterface;
Expand All @@ -29,23 +28,27 @@ class ConfigurablePriceResolver implements PriceResolverInterface
protected $configurable;

/**
* @var ConfigurableOptionsProviderInterface
* @var LowestPriceOptionsProviderInterface
*/
private $configurableOptionsProvider;
private $lowestPriceOptionsProvider;

/**
* @param PriceResolverInterface $priceResolver
* @param Configurable $configurable
* @param PriceCurrencyInterface $priceCurrency
* @param LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider
*/
public function __construct(
PriceResolverInterface $priceResolver,
Configurable $configurable,
PriceCurrencyInterface $priceCurrency
PriceCurrencyInterface $priceCurrency,
LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider = null
) {
$this->priceResolver = $priceResolver;
$this->configurable = $configurable;
$this->priceCurrency = $priceCurrency;
$this->lowestPriceOptionsProvider = $lowestPriceOptionsProvider ?:
ObjectManager::getInstance()->get(LowestPriceOptionsProviderInterface::class);
}

/**
Expand All @@ -57,7 +60,7 @@ public function resolvePrice(\Magento\Framework\Pricing\SaleableInterface $produ
{
$price = null;

foreach ($this->getConfigurableOptionsProvider()->getProducts($product) as $subProduct) {
foreach ($this->lowestPriceOptionsProvider->getProducts($product) as $subProduct) {
$productPrice = $this->priceResolver->resolvePrice($subProduct);
$price = $price ? min($price, $productPrice) : $productPrice;
}
Expand All @@ -69,17 +72,4 @@ public function resolvePrice(\Magento\Framework\Pricing\SaleableInterface $produ

return (float)$price;
}

/**
* @return \Magento\ConfigurableProduct\Pricing\Price\ConfigurableOptionsProviderInterface
* @deprecated
*/
private function getConfigurableOptionsProvider()
{
if (null === $this->configurableOptionsProvider) {
$this->configurableOptionsProvider = ObjectManager::getInstance()
->get(ConfigurableOptionsProviderInterface::class);
}
return $this->configurableOptionsProvider;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,31 @@ class ConfigurableRegularPrice extends AbstractPrice implements ConfigurableRegu
*/
private $configurableOptionsProvider;

/**
* @var LowestPriceOptionsProviderInterface
*/
private $lowestPriceOptionsProvider;

/**
* @param \Magento\Framework\Pricing\SaleableInterface $saleableItem
* @param float $quantity
* @param \Magento\Framework\Pricing\Adjustment\CalculatorInterface $calculator
* @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
* @param PriceResolverInterface $priceResolver
* @param LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider
*/
public function __construct(
\Magento\Framework\Pricing\SaleableInterface $saleableItem,
$quantity,
\Magento\Framework\Pricing\Adjustment\CalculatorInterface $calculator,
\Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
PriceResolverInterface $priceResolver
PriceResolverInterface $priceResolver,
LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider = null
) {
parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency);
$this->priceResolver = $priceResolver;
$this->lowestPriceOptionsProvider = $lowestPriceOptionsProvider ?:
ObjectManager::getInstance()->get(LowestPriceOptionsProviderInterface::class);
}

/**
Expand Down Expand Up @@ -88,15 +97,14 @@ public function getAmount()
public function getMaxRegularAmount()
{
if (null === $this->maxRegularAmount) {
$this->maxRegularAmount = $this->doGetMaxRegularAmount();
$this->maxRegularAmount = $this->doGetMaxRegularAmount() ?: false;
}
return $this->maxRegularAmount;

}

/**
* Get max regular amount. Template method
* Get max regular amount
*
* @return \Magento\Framework\Pricing\Amount\AmountInterface
*/
Expand Down Expand Up @@ -124,14 +132,14 @@ public function getMinRegularAmount()
}

/**
* Get min regular amount. Template method
* Get min regular amount
*
* @return \Magento\Framework\Pricing\Amount\AmountInterface
*/
protected function doGetMinRegularAmount()
{
$minAmount = null;
foreach ($this->getUsedProducts() as $product) {
foreach ($this->lowestPriceOptionsProvider->getProducts($this->product) as $product) {
$childPriceAmount = $product->getPriceInfo()->getPrice(self::PRICE_CODE)->getAmount();
if (!$minAmount || ($childPriceAmount->getValue() < $minAmount->getValue())) {
$minAmount = $childPriceAmount;
Expand Down

0 comments on commit eacec0e

Please sign in to comment.