You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

装饰ProductPriceCalculator后购物车价格重复相乘问题

问题:装饰ProductPriceCalculator后购物车价格多次相乘

按照Shopware官方文档装饰ProductPriceCalculator后,商品详情页的自定义价格计算功能正常,但将商品加入购物车后,价格会被多次乘以自定义系数,导致价格异常。

用户实现的代码如下:

public function calculate(iterable $products, SalesChannelContext $context): void
{
    /** @var SalesChannelProductEntity $product */
    foreach ($products as $product) {
        if(array_key_exists('custom_calculation', $product->getCustomFields())
            && $product->getCustomFields()['custom_calculation'] != '') {
            $factor = (float)$product->getCustomFields()['custom_calculation'];

            $price = $product->getPrice();
            $price->first()->setGross($price->first()->getGross() * $factor);
            $price->first()->setNet($price->first()->getNet() * $factor);
        }
    }

    $this->getDecorated()->calculate($products, $context);
}

问题原因

Shopware在购物车操作(添加商品、刷新购物车、结算等场景)中会多次调用ProductPriceCalculator::calculate方法。当前代码直接修改了产品实体的价格对象,而同一个产品实体实例可能被重复传入该方法,导致每次调用都会基于已经修改过的价格再次乘以系数,最终出现价格多次相乘的问题。

解决方案

需要确保自定义价格计算仅基于产品的原始价格执行一次,避免重复计算。以下提供两种可行方案:

方案1:基于原始价格计算(推荐)

从产品的原始数据库数据中获取未修改的价格,以此为基础计算新价格,避免基于已修改的价格重复相乘:

public function calculate(iterable $products, SalesChannelContext $context): void
{
    /** @var SalesChannelProductEntity $product */
    foreach ($products as $product) {
        if(array_key_exists('custom_calculation', $product->getCustomFields())
            && $product->getCustomFields()['custom_calculation'] != '') {
            $factor = (float)$product->getCustomFields()['custom_calculation'];
            
            // 获取产品的原始数据库价格(未经过销售渠道和自定义修改的价格)
            $originalProduct = $product->getProduct();
            if ($originalProduct && $originalProduct->getPrice()) {
                $originalPrice = $originalProduct->getPrice()->first();
                $newGross = $originalPrice->getGross() * $factor;
                $newNet = $originalPrice->getNet() * $factor;
                
                // 更新销售渠道产品的价格
                $salesChannelPrice = $product->getPrice()->first();
                $salesChannelPrice->setGross($newGross);
                $salesChannelPrice->setNet($newNet);
            }
        }
    }

    $this->getDecorated()->calculate($products, $context);
}

方案2:添加临时标记避免重复计算

给产品实体添加临时扩展标记,标记自定义价格是否已应用,避免重复执行计算逻辑:

use Shopware\Core\Framework\Struct\ArrayStruct;

public function calculate(iterable $products, SalesChannelContext $context): void
{
    /** @var SalesChannelProductEntity $product */
    foreach ($products as $product) {
        // 检查是否已应用过自定义价格计算,跳过重复执行
        if ($product->hasExtension('custom_price_applied')) {
            continue;
        }
        
        if(array_key_exists('custom_calculation', $product->getCustomFields())
            && $product->getCustomFields()['custom_calculation'] != '') {
            $factor = (float)$product->getCustomFields()['custom_calculation'];

            $price = $product->getPrice()->first();
            $price->setGross($price->getGross() * $factor);
            $price->setNet($price->getNet() * $factor);
            
            // 添加临时扩展标记,标记已完成自定义价格计算
            $product->addExtension('custom_price_applied', new ArrayStruct(['applied' => true]));
        }
    }

    $this->getDecorated()->calculate($products, $context);
}

说明

  • 方案1通过关联的原始产品实体获取未修改的价格,计算逻辑更可靠,适合大多数场景。
  • 方案2的临时标记仅在当前实体实例生命周期内有效,若产品实体被重新加载(如购物车刷新时重新从数据库获取),标记会丢失,可能需要结合其他逻辑补充。

内容的提问来源于stack exchange,提问作者DannyDan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 16:33:39