Add ‘Price From’ to Configurable Products with Different Prices in Magento 2

configurable-productmagento2

How to add a string like "Price from" next to configurable product prices on Category, Search and Product pages, but only if the configurable product has simple products with different prices in it?

Best Answer

I did this by extending Magento\ConfigurableProduct\Pricing\Price\FinalPrice with functions for getting min/max final price of configurable products and inserting the label only when needed (when there are simple products with different prices inside the configurable).

Gonna post my solution for others that might need help with this:

{Vendor}/{Module}/etc/di.xml

<preference for="Magento\ConfigurableProduct\Pricing\Price\FinalPrice" type="{Vendor}\{Module}\Pricing\Price\FinalPrice"/>

{Vendor}/{Module}/Pricing/Price/FinalPrice

<?php
namespace Vendor\Module\Pricing\Price;

use Magento\Framework\App\ObjectManager;

class FinalPrice extends \Magento\ConfigurableProduct\Pricing\Price\FinalPrice
{

    protected $priceResolver;

    protected $maxFinalAmount;

    protected $minFinalAmount;

    protected $lowestPriceOptionsProvider;

    protected $configurableType;

    public function __construct(
        \Magento\Framework\Pricing\SaleableInterface $saleableItem,
        $quantity,
        \Magento\Framework\Pricing\Adjustment\CalculatorInterface $calculator,
        \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
        \Magento\ConfigurableProduct\Pricing\Price\PriceResolverInterface $priceResolver,
        \Magento\ConfigurableProduct\Pricing\Price\LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider = null,
        \Magento\ConfigurableProduct\Model\Product\Type\Configurable $configurableType
    ) {
        parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency, $priceResolver);
        $this->priceResolver = $priceResolver;
        $this->lowestPriceOptionsProvider = $lowestPriceOptionsProvider ?:
        ObjectManager::getInstance()->get(\Magento\ConfigurableProduct\Pricing\Price\LowestPriceOptionsProviderInterface::class);
        $this->configurableType = $configurableType ?:
        ObjectManager::getInstance()->get(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::class);
    }

    public function getMaxFinalAmount()
    {
        if (null === $this->maxFinalAmount) {
            $this->maxFinalAmount = $this->doGetMaxFinalAmount() ?: false;
        }
        return $this->maxFinalAmount;
    }

    protected function doGetMaxFinalAmount()
    {
        $maxAmount = null;
        $usedProducts = $this->configurableType->getUsedProducts($this->product);

        foreach ($usedProducts as $product) {
            $childPriceAmount = $product->getPriceInfo()->getPrice('final_price')->getAmount();
            if (!$maxAmount || ($childPriceAmount->getValue() > $maxAmount->getValue())) {
                $maxAmount = $childPriceAmount;
            }
        }
        return $maxAmount;
    }

    public function getMinFinalAmount()
    {
        if (null === $this->minFinalAmount) {
            $this->minFinalAmount = $this->doGetMinFinalAmount() ?: parent::getAmount();
        }
        return $this->minFinalAmount;
    }

    protected function doGetMinFinalAmount()
    {
        $minAmount = null;
        $usedProducts = $this->lowestPriceOptionsProvider->getProducts($this->product);

        foreach ($usedProducts as $product) {
            $childPriceAmount = $product->getPriceInfo()->getPrice('final_price')->getAmount();
            if (!$minAmount || ($childPriceAmount->getValue() < $minAmount->getValue())) {
                $minAmount = $childPriceAmount;
            }
        }
        return $minAmount;
    }
}

Then you can call the new function getMinFinalAmount and getMaxFinalAmount from your custom:

.../Magento_ConfigurableProduct/templates/product/price/final_price.phtml

$minAmount = $finalPriceModel->getMinFinalAmount();
$maxAmount = $finalPriceModel->getMaxFinalAmount();

And display the label where you want like this:

<?php if ($minAmount < $maxAmount) { ?>
    <span class="price-label"><?php echo __('Price from:') ?></span>
<?php } ?>

All that's left is to use JS to hide the Price from label when a simple configuration is selected.

That depends on if you're using swatches or not.

To remove the label by using swatches, you need to edit Magento_Swatches/web/js/swatch-renderer.js.

I added this code to the _UpdatePrice function which is executed when a swatch is clicked.

$product.find('.price-label').hide(0);

To remove the label by using ordinary configurable options, you need to edit Magento_ConfigurableProduct/web/js/configurable.js.

I added this code to the _calculatePrice function which is executed when a configurable option is selected from the dropdown.

$(this.options.priceHolderSelector).find('.price-label').hide(0);

Related Topic