Customize Price Calculation
This guide explains how to globally adjust the calculation of product prices by decorating a single service, and includes a brief example.
Prerequisites
Review the plugin base guide and the guide on adjusting a service for guidance on service decoration.
Decorating the calculator
In order to customize the price calculation for products as a whole, you'll have to decorate the service ProductPriceCalculator. It comes with a calculate method, which you can decorate and therefore customize.
So let's do that real quick. If you're looking for an in-depth explanation, head over to our guide about adjusting a service.
Here's an example decorated calculator:
// <plugin root>/src/Service/CustomProductPriceCalculator.php
<?php declare(strict_types=1);
namespace Swag\BasicExample\Service;
use Shopware\Core\Content\Product\SalesChannel\Price\AbstractProductPriceCalculator;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
class CustomProductPriceCalculator extends AbstractProductPriceCalculator
{
/**
* @var AbstractProductPriceCalculator
*/
private AbstractProductPriceCalculator $productPriceCalculator;
public function __construct(AbstractProductPriceCalculator $productPriceCalculator)
{
$this->productPriceCalculator = $productPriceCalculator;
}
public function getDecorated(): AbstractProductPriceCalculator
{
return $this->productPriceCalculator;
}
public function calculate(iterable $products, SalesChannelContext $context): void
{
/** @var SalesChannelProductEntity $product */
foreach ($products as $product) {
$price = $product->getPrice();
// Just an example!
// A product can have more than one price, which you also have to consider.
// Also you might have to change the value of "getCheapestPrice"!
$price->first()->setGross(100);
$price->first()->setNet(50);
}
$this->getDecorated()->calculate($products, $context);
}
}So what is done here? The constructor gets passed the inner instance of AbstractProductPriceCalculator, most likely the ProductPriceCalculator itself. This will be used to call the original calculate method later on. You also have to return that instance in your getDecorated method.
Inside the overridden calculate method, we're iterating over each product and we straight forward set new prices. Of course this is just an example to show how you can now manipulate a product's prices.
Most likely you also want to narrow down which product's prices you want to edit, as in this example we're adjusting every single product and setting them all to the same price. You might want to have a look at the original calculate method to see how calculating a price is done in the core code.
Registering the decorator
Do not forget to actually register your decoration to the service container, otherwise it will not have any effect.
// <plugin root>/src/Resources/config/services.php
<?php declare(strict_types=1);
use Shopware\Core\Content\Product\SalesChannel\Price\ProductPriceCalculator;
use Swag\BasicExample\Service\CustomProductPriceCalculator;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
return static function (ContainerConfigurator $configurator): void {
$services = $configurator->services();
$services->set(CustomProductPriceCalculator::class)
->decorate(ProductPriceCalculator::class)
->args([
service('.inner'),
]);
};Next steps
Instead of manipulating a product's price, you can also try to add a discount or a surcharge to the cart. This is explained in our guide about adding cart discounts.