如何实现单个客户多购物车功能,并按商品自定义字段分配对应购物车
解决方案
你遇到的限制是BeforeLineItemAddedEvent触发时,当前请求已经绑定了固定的购物车实例,事件本身没有提供切换目标购物车的能力,所以这个思路走不通。推荐你用装饰核心CartService的方案实现,这是Shopware官方推荐的扩展购物车逻辑的最优方案,不需要改前端默认逻辑,全后端处理即可:
步骤1:配置服务装饰
在你的插件的src/Resources/config/services.xml里添加装饰配置:
<service id="YourPluginName\Decorator\CartServiceDecorator" decorates="Shopware\Core\Checkout\Cart\SalesChannel\CartService"> <argument type="service" id="YourPluginName\Decorator\CartServiceDecorator.inner"/> <argument type="service" id="Shopware\Core\Content\Product\ProductRepository"/> </service>
步骤2:实现装饰类逻辑
<?php declare(strict_types=1); namespace YourPluginName\Decorator; use Shopware\Core\Checkout\Cart\Cart; use Shopware\Core\Checkout\Cart\LineItem\LineItem; use Shopware\Core\Checkout\Cart\SalesChannel\CartService; use Shopware\Core\Content\Product\ProductEntity; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\System\SalesChannel\SalesChannelContext; class CartServiceDecorator extends CartService { public function __construct( private readonly CartService $innerService, private readonly \Shopware\Core\Content\Product\ProductRepository $productRepository ) {} public function add(Cart $cart, LineItem $lineItem, SalesChannelContext $context): Cart { // 只处理商品类型的行项目 if ($lineItem->getType() !== LineItem::PRODUCT_LINE_ITEM_TYPE) { return $this->innerService->add($cart, $lineItem, $context); } // 加载商品自定义字段 /** @var ProductEntity $product */ $product = $this->productRepository->search( new Criteria([$lineItem->getReferencedId()]), $context->getContext() )->first(); $cartType = $product->getCustomFields()['cart-type'] ?? 'default'; // 目标购物车和当前传入购物车类型一致,直接走原有逻辑 $currentCartType = $cart->getExtension('cartType')?->get('type') ?? 'default'; if ($cartType === $currentCartType) { return $this->innerService->add($cart, $lineItem, $context); } // 获取对应类型的购物车,不存在则创建 $targetCart = $this->getCartByType($cartType, $context); return $this->innerService->add($targetCart, $lineItem, $context); } private function getCartByType(string $cartType, SalesChannelContext $context): Cart { // 你可以把客户的不同购物车token存在customer的扩展字段里,或者用自定义存储表 // 这里给个示例逻辑: $customer = $context->getCustomer(); $cartTokens = $customer?->getCustomFields()['cart_tokens'] ?? []; if (isset($cartTokens[$cartType])) { return $this->innerService->getCart($cartTokens[$cartType], $context); } // 不存在对应类型购物车,创建新的 $newCart = $this->innerService->createNew($context); $newCart->addExtension('cartType', new \Shopware\Core\Framework\Struct\ArrayStruct(['type' => $cartType])); // 保存新购物车的token到客户自定义字段 $cartTokens[$cartType] = $newCart->getToken(); $this->productRepository->getRepository('customer')->update([ [ 'id' => $customer->getId(), 'customFields' => [ 'cart_tokens' => $cartTokens ] ] ], $context->getContext()); return $newCart; } // 你需要把CartService的其他所有公共方法都透传给innerService,这里省略其他方法的实现,直接调用$this->innerService->对应方法即可 }
配套前端调整
前端侧边购物车的下拉切换逻辑,只需要调用CartService的getCart方法传入对应类型的购物车token即可拿到对应购物车的数据,不需要改核心购物车渲染逻辑。
内容的提问来源于stack exchange,提问作者Roman
相关产品推荐
相关产品推荐

