I use Magento version 2.4.5-p10
I want show on product card on category page (list and grid) the minimal price for a configurable product when this price has special price.
The objective is to show the mention : %special price% Instead of %Original price%
In file src/app/code/Custom/Catalog/view/frontend/templates/product/list.phtml at line 303 I have call to <?= /* @noEscape */ $block->getProductPrice($product); ?>
This call use src/vendor/magento/module-catalog/Block/Product/ListProduct.php
/**
* Get product price.
*
* @param Product $product
* @return string
*/
public function getProductPrice(Product $product)
{
$priceRender = $this->getPriceRender();
$price = '';
if ($priceRender) {
$price = $priceRender->render(
FinalPrice::PRICE_CODE,
$product,
[
'include_container' => true,
'display_minimal_price' => true,
'zone' => Render::ZONE_ITEM_LIST,
'list_category_page' => true
]
);
}
return $price;
}
src/app/code/Custom/ConfigurableProduct/view/frontend/templates/product/price/final_price.phtml
<?php if($block->hasSpecialPrice()): ?>
<span class="old-price">
<?= /* @noEscape */ $block->renderAmount(
$block->getMinimalChildPriceAMount(),
$block->getOldPriceArguments()
); ?>
</span>
<?php endif; ?>
$block is src/app/code/Custom/ConfigurableProduct/Pricing/Render/FinalPriceBox.php
In src/app/code/Custom/ConfigurableProduct/Pricing/Render/FinalPriceBox.php, I have
<?php
/**
* Class FinalPriceBox.
*
*/
declare(strict_types=1);
namespace CustomConfigurableProductPricingRender;
use CustomCatalogApiPricingRenderFinalPriceBoxEcoInterface;
use CustomCatalogHelperPricingRenderEcoPriceHelper;
use CustomCustomerHelperStoreHelper;
use MagentoCatalogModelProductPricingRendererSalableResolverInterface;
use MagentoCatalogPricingPriceFinalPrice;
use MagentoCatalogPricingPriceMinimalPriceCalculatorInterface;
use MagentoCatalogPricingPriceRegularPrice;
use MagentoConfigurableProductPricingPriceConfigurableOptionsProviderInterface;
use MagentoConfigurableProductPricingRenderFinalPriceBox as MagentoFinalPriceBox;
use MagentoFrameworkExceptionNoSuchEntityException;
use MagentoFrameworkPhrase;
use MagentoFrameworkPricingAmountBase;
use MagentoFrameworkPricingPricePriceInterface;
use MagentoFrameworkPricingRenderRendererPool;
use MagentoFrameworkPricingSaleableInterface;
use MagentoFrameworkViewElementTemplateContext;
use MagentoTaxApiTaxCalculationInterface;
/**
* Configurable product final price box.
*
* CustomConfigurableProductPricingRender
*/
class FinalPriceBox extends MagentoFinalPriceBox implements FinalPriceBoxEcoInterface
{
/**
* @var EcoPriceHelper
*/
protected $ecoPriceHelper;
/**
* @var ConfigurableOptionsProviderInterface
*/
private $configurableOptionsProvider;
/**
* @var MagentoTaxApiTaxCalculationInterface
*/
private $TaxCalculationInterface;
/**
* @var StoreHelper
*/
protected $storeHelper;
/**
* Configurable product final price box block constructor.
*
* @param Context $context
* @param SaleableInterface $saleableItem
* @param PriceInterface $price
* @param RendererPool $rendererPool
* @param SalableResolverInterface $salableResolver
* @param MinimalPriceCalculatorInterface $minimalPriceCalculator
* @param ConfigurableOptionsProviderInterface $configurableOptionsProvider
* @param EcoPriceHelper $ecoPriceHelper
* @param TaxCalculationInterface $TaxCalculationInterface
* @param StoreHelper $storeHelper
* @param array $data
*/
public function __construct(
Context $context,
SaleableInterface $saleableItem,
PriceInterface $price,
RendererPool $rendererPool,
SalableResolverInterface $salableResolver,
MinimalPriceCalculatorInterface $minimalPriceCalculator,
ConfigurableOptionsProviderInterface $configurableOptionsProvider,
EcoPriceHelper $ecoPriceHelper,
TaxCalculationInterface $TaxCalculationInterface,
storeHelper $storeHelper,
array $data = []
) {
parent::__construct(
$context,
$saleableItem,
$price,
$rendererPool,
$salableResolver,
$minimalPriceCalculator,
$configurableOptionsProvider,
$data
);
$this->ecoPriceHelper = $ecoPriceHelper;
$this->configurableOptionsProvider = $configurableOptionsProvider;
$this->TaxCalculationInterface = $TaxCalculationInterface;
$this->storeHelper = $storeHelper;
}
/**
* @inheritDoc
*/
public function isVisibleEcoPrice(): bool
{
return $this->ecoPriceHelper->isVisibleEcoPrice($this->saleableItem->getId(), $this->getZone());
}
/**
* @inheritDoc
*
* @throws NoSuchEntityException
*/
public function getEcoText(bool $oldPriceExists = false): Phrase
{
if ($oldPriceExists) {
return $this->ecoPriceHelper->getEcoTextForOldPrice($this->saleableItem->getId());
}
return $this->ecoPriceHelper->getEcoText($this->saleableItem->getId());
}
/**
* Returns true if it is TS store.
*
* @return bool
*/
public function isTs(): bool
{
try {
$result = $this->storeHelper->isTs();
} catch (NoSuchEntityException $exception) {
return false;
}
return $result;
}
/**
* Returns old price arguments.
*
* @return array
*/
public function getOldPriceArguments(): array
{
$idSuffix = $this->getIdSuffix() ?: '';
$arguments = [
'display_label' => __('Instead of'),
'price_id' => $this->getPriceId('old-price-' . $idSuffix),
'price_type' => 'oldPrice',
'include_container' => true,
];
if (!$this->isTs()) {
$arguments['skip_adjustments'] = true;
}
return $arguments;
}
/**
* Return Minimal Child Price Amount
*
* @return Base|bool
*/
public function getMinimalChildPriceAMount() : Base|bool
{
$prices = $this->getChildPrices();
/** filter cheapest child product */
$cheapestChildId = array_reduce(array_keys($prices), function ($prev, $id) use ($prices) {
return ($prev === null || $prices[$id]['regular_price'] < $prices[$prev]['regular_price']) ? $id : $prev;
}, null);
/** Valid only if the cheapest child (which serves as call price) is on promotion */
$regularPrice = $prices[$cheapestChildId][RegularPrice::PRICE_CODE];
$finalPrice = $prices[$cheapestChildId][FinalPrice::PRICE_CODE];
if ($finalPrice < $regularPrice) {
$amount = new Base(
$finalPrice,
[
'tax' => ($finalPrice * $this->getTaxRate())
]
);
return $amount;
}
return false;
}
/**
* Return [subproduct_sku => [regular_price, final_price]] for the current configurable parent product
*/
private function getChildPrices(): array
{
$product = $this->getSaleableItem();
$prices = [];
foreach ($this->configurableOptionsProvider->getProducts($product) as $subProduct) {
$regularPrice = $subProduct->getPriceInfo()->getPrice(RegularPrice::PRICE_CODE)->getValue();
$finalPrice = $subProduct->getPriceInfo()->getPrice(FinalPrice::PRICE_CODE)->getValue();
$prices[$subProduct->getSku()] =
[
RegularPrice::PRICE_CODE => $regularPrice,
FinalPrice::PRICE_CODE => $finalPrice
];
}
return $prices;
}
/**
* Return Tax Rate for the current product
*
* @return float
*/
private function getTaxRate(): float
{
return $this->TaxCalculationInterface->getCalculatedRate(
$this->getSaleableItem()->getTaxClassId()
);
}
}
In frontend https://custom.local/store_code/path/to/category/with/configurable-products.html (after setup:upgrade) I’m block by the error
Error: Call to undefined method ReflectionUnionType::getName() in /var/www/html/vendor/magento/framework/Interception/Code/Generator/Interceptor.php:216
Stack trace:
#0 /var/www/html/vendor/magento/framework/Interception/Code/Generator/Interceptor.php(105): MagentoFrameworkInterceptionCodeGeneratorInterceptor->getReturnTypeValue(Object(ReflectionMethod))
#1 /var/www/html/vendor/magento/framework/Interception/Code/Generator/Interceptor.php(76): MagentoFrameworkInterceptionCodeGeneratorInterceptor->_getMethodInfo(Object(ReflectionMethod))
#2 /var/www/html/vendor/magento/framework/Code/Generator/EntityAbstract.php(228): MagentoFrameworkInterceptionCodeGeneratorInterceptor->_getClassMethods()
#3 /var/www/html/vendor/magento/framework/Interception/Code/Generator/Interceptor.php(177): MagentoFrameworkCodeGeneratorEntityAbstract->_generateCode()
#4 /var/www/html/vendor/magento/framework/Code/Generator/EntityAbstract.php(108): MagentoFrameworkInterceptionCodeGeneratorInterceptor->_generateCode()
#5 /var/www/html/vendor/magento/framework/Code/Generator.php(130): MagentoFrameworkCodeGeneratorEntityAbstract->generate()
#6 /var/www/html/vendor/magento/framework/Code/Generator/Autoloader.php(53): MagentoFrameworkCodeGenerator->generateClass('Custom\Configur...')
#7 [internal function]: MagentoFrameworkCodeGeneratorAutoloader->load('Custom\Configur...')
#8 /var/www/html/vendor/magento/framework/Code/Reader/ClassReader.php(34): ReflectionClass->__construct('Custom\Configur...')
#9 /var/www/html/vendor/magento/framework/ObjectManager/Definition/Runtime.php(54): MagentoFrameworkCodeReaderClassReader->getConstructor('Custom\Configur...')
#10 /var/www/html/vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php(48): MagentoFrameworkObjectManagerDefinitionRuntime->getParameters('Custom\Configur...')
#11 /var/www/html/vendor/magento/framework/ObjectManager/ObjectManager.php(56): MagentoFrameworkObjectManagerFactoryDynamicDeveloper->create('Custom\Configur...', Array)
#12 /var/www/html/vendor/magento/framework/View/Element/BlockFactory.php(44): MagentoFrameworkObjectManagerObjectManager->create('Custom\Configur...', Array)
#13 /var/www/html/vendor/magento/framework/View/Layout/Generator/Block.php(272): MagentoFrameworkViewElementBlockFactory->createBlock('Custom\Configur...', Array)
#14 /var/www/html/vendor/magento/framework/View/Layout/Generator/Block.php(252): MagentoFrameworkViewLayoutGeneratorBlock->getBlockInstance('Custom\Configur...', Array)
#15 /var/www/html/vendor/magento/framework/View/Layout.php(797): MagentoFrameworkViewLayoutGeneratorBlock->createBlock('Custom\Configur...', 'custom\configur...', Array)
#16 /var/www/html/vendor/magento/framework/View/Layout.php(780): MagentoFrameworkViewLayout->_createBlock('Custom\Configur...', 'custom\configur...', Array)
#17 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(58): MagentoFrameworkViewLayout->createBlock('Custom\Configur...', 'custom\configur...', Array)
#18 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(138): MagentoFrameworkViewLayoutInterceptor->___callParent('createBlock', Array)
#19 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(153): MagentoFrameworkViewLayoutInterceptor->MagentoFrameworkInterception{closure}('Custom\Configur...', '', Array)
#20 /var/www/html/generated/code/Magento/Framework/View/Layout/Interceptor.php(248): MagentoFrameworkViewLayoutInterceptor->___callPlugins('createBlock', Array, Array)
#21 /var/www/html/vendor/magento/framework/Pricing/Render/RendererPool.php(78): MagentoFrameworkViewLayoutInterceptor->createBlock('Custom\Configur...', '', Array)
#22 /var/www/html/vendor/magento/framework/Pricing/Render.php(96): MagentoFrameworkPricingRenderRendererPool->createPriceRender('final_price', Object(MagentoCatalogModelProductInterceptor), Array)
#23 /var/www/html/vendor/magento/module-catalog/Block/Product/ListProduct.php(411): MagentoFrameworkPricingRender->render('final_price', Object(MagentoCatalogModelProductInterceptor), Array)
#24 /var/www/html/generated/code/Magento/Catalog/Block/Product/ListProduct/Interceptor.php(131): MagentoCatalogBlockProductListProduct->getProductPrice(Object(MagentoCatalogModelProductInterceptor))
#25 /var/www/html/app/code/Custom/Catalog/view/frontend/templates/product/list.phtml(303): MagentoCatalogBlockProductListProductInterceptor->getProductPrice(Object(MagentoCatalogModelProductInterceptor))
The error is located in https://github.com/magento/magento2/blob/ca0c22e48ad5adb5726b5a949a702e99937c04c3/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php#L216
Where I made mistake in src/app/code/Custom/ConfigurableProduct/view/frontend/templates/product/price/final_price.phtml or in src/app/code/Custom/ConfigurableProduct/Pricing/Render/FinalPriceBox.php ?