Shopware6插件开发:如何从已有商品创建并添加LineItem至购物车?
Shopware6插件:从已有商品创建LineItem并添加至购物车的正确实现
核心问题分析
- 无限循环:调用
CartService->add会再次触发AfterLineItemAddedEvent,导致代码反复执行。 - 商品未找到:直接通过
LineItem构造函数创建商品类型条目,缺少销售渠道关联、商品价格等必要元数据,购物车持久化时无法识别。
解决方案一:事件中添加循环防护(快速修复)
在事件回调里先判断当前触发事件的条目是否为手续费商品,避免循环;同时用ProductLineItemFactory创建符合规范的商品条目:
use Shopware\Core\Checkout\Cart\LineItem\LineItem; use Shopware\Core\Checkout\Cart\LineItem\Factory\ProductLineItemFactory; use Shopware\Core\Checkout\Cart\Event\AfterLineItemAddedEvent; class YourCartSubscriber { private $cartService; private $productRepository; private $productLineItemFactory; public function __construct( \Shopware\Core\Checkout\Cart\CartService $cartService, \Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface $productRepository, ProductLineItemFactory $productLineItemFactory ) { $this->cartService = $cartService; $this->productRepository = $productRepository; $this->productLineItemFactory = $productLineItemFactory; } public function onItemAdded(AfterLineItemAddedEvent $event): void { $addedItem = $event->getLineItem(); // 防护:如果当前添加的是手续费商品,直接返回避免循环 if ($addedItem->getType() === LineItem::PRODUCT_LINE_ITEM_TYPE && ($addedItem->getPayload()['productNumber'] ?? '') === 'PP') { return; } $cart = $event->getCart(); // 检查购物车是否已存在手续费商品 $hasFeeItem = $cart->getLineItems()->filter(function (LineItem $item) { return ($item->getPayload()['productNumber'] ?? '') === 'PP'; })->count() > 0; if ($hasFeeItem) { return; } // 查询手续费商品(关联销售渠道确保可用) $criteria = new \Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria(); $criteria->addFilter(new \Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter('productNumber', 'PP')); $criteria->addAssociation('salesChannels'); $product = $this->productRepository->search($criteria, $event->getSalesChannelContext()->getContext())->first(); if (!$product) { return; } // 用工厂创建标准商品LineItem $feeLineItem = $this->productLineItemFactory->create( $product, $event->getSalesChannelContext(), 1, 'paypalfee' ); $feeLineItem->setRemovable(false) ->setStackable(false); // 添加并更新购物车 $cart->add($feeLineItem); $this->cartService->update($cart, $event->getSalesChannelContext()); } }
解决方案二:使用CartProcessor(官方推荐)
Shopware推荐通过CartProcessor接口修改购物车,这是更规范的方式,不会触发事件循环:
1. 创建CartProcessor类
use Shopware\Core\Checkout\Cart\Cart; use Shopware\Core\Checkout\Cart\CartProcessorInterface; use Shopware\Core\Checkout\Cart\LineItem\LineItem; use Shopware\Core\Checkout\Cart\LineItem\Factory\ProductLineItemFactory; use Shopware\Core\Checkout\Cart\SalesChannel\CartContext; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface; class PayPalFeeCartProcessor implements CartProcessorInterface { private $productRepository; private $productLineItemFactory; public function __construct( EntityRepositoryInterface $productRepository, ProductLineItemFactory $productLineItemFactory ) { $this->productRepository = $productRepository; $this->productLineItemFactory = $productLineItemFactory; } public function process(Cart $cart, CartContext $context): void { // 检查是否已存在手续费商品 $hasFeeItem = $cart->getLineItems()->filter(function (LineItem $item) { return ($item->getPayload()['productNumber'] ?? '') === 'PP'; })->count() > 0; if ($hasFeeItem) { return; } // 查询手续费商品 $criteria = new \Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria(); $criteria->addFilter(new \Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter('productNumber', 'PP')); $product = $this->productRepository->search($criteria, $context->getContext())->first(); if (!$product) { return; } // 创建商品LineItem $feeLineItem = $this->productLineItemFactory->create( $product, $context->getSalesChannelContext(), 1, 'paypalfee' ); $feeLineItem->setRemovable(false) ->setStackable(false); // 根据支付方式调整价格(非PayPal时金额设为0) $paymentMethodId = $context->getSalesChannelContext()->getPaymentMethod()->getId(); if ($paymentMethodId !== '你的PayPal支付方式ID') { $feeLineItem->setPrice(new \Shopware\Core\Checkout\Cart\Price\Price(0, 0, [])); } $cart->add($feeLineItem); } }
2. 注册CartProcessor
在插件的services.xml中注册服务:
<service id="YourPlugin\Cart\PayPalFeeCartProcessor"> <argument type="service" id="product.repository"/> <argument type="service" id="Shopware\Core\Checkout\Cart\LineItem\Factory\ProductLineItemFactory"/> <tag name="shopware.cart.processor" priority="100"/> </service>
额外注意事项
- 确保手续费商品
PP已关联到当前销售渠道,否则会查询失败。 - 动态调整价格时,在
CartProcessor中处理是最优方案,因为每次购物车计算都会执行逻辑。 - 不要直接调用
CartPersister保存购物车,通过CartService->update处理会自动完成持久化和必要事件触发。
内容的提问来源于stack exchange,提问作者Sistason
相关产品推荐
相关产品推荐

