登录前购物车里有一件蓝色M号T恤,客户原来的购物车里也有同一件。登录后预期数量变成2,结果页面显示两行,而且第二行改数量时第一行也偶尔变化。Magento合并Quote Item不是只比较SKU,它会比较product configuration、自定义option和buyRequest;看起来一样的两行,底层选项里常常多了一个空值或不同类型的值。

先把两行的真实配置打印出来

不要在模板里var_dump。做一个临时CLI命令或在安全的调试环境中读取Quote,记录item_id、sku、product_type、parent_item_id和option列表。

$quote = $quoteRepository->getActive($cartId);
foreach ($quote->getAllVisibleItems() as $item) {
    echo $item->getId() . " " . $item->getSku() . PHP_EOL;
    foreach ($item->getOptions() as $option) {
        echo $option->getCode() . "=" . $option->getValue() . PHP_EOL;
    }
}

重点比较 info_buyRequest、attributes、simple_product、additional_options。常见差异是一个写成字符串"12",另一个是整数12;一个包含空的自定义字段,另一个根本没有该字段;第三方插件还可能把时间戳、来源渠道或显示文案写入additional_options,导致每次都被视为不同商品。

别直接改Quote Item Compare核心类

正确做法是在加入购物车之前规范化自定义请求,或者只把真正影响商品配置的字段保存为option。展示用途的数据可以放extension attribute或单独表,不要把随机值塞进buyRequest。

$request->setData("engraving", trim((string)$request->getData("engraving")));
if ($request->getData("gift_message") === "") {
    $request->unsetData("gift_message");
}
$request->setData("super_attribute", array_map("intval", $request->getData("super_attribute", [])));

已有脏购物车怎么处理

不要用SQL直接合并quote_item。通过Cart或Quote服务逐项比较规范化配置:配置相同则把目标item数量相加并删除来源item;配置不同就保留两行。完成后调用collectTotals并保存Quote。操作前必须确认Quote未进入下单流程,避免和placeOrder并发。

$target->setQty($target->getQty() + $source->getQty());
$quote->removeItem($source->getId());
$quote->setTotalsCollectedFlag(false)->collectTotals();
$quoteRepository->save($quote);

如果重复只发生在REST或GraphQL,检查前端发送的selected_options、entered_options以及服务端转换后的buyRequest。PWA端经常把空entered_options数组和完全不传字段当成两种状态。

回归用例要覆盖登录合并

  • 游客和客户购物车各有同一simple商品,登录后数量合并
  • 相同可配置选项应合并,不同颜色或尺码必须保留两行
  • 相同SKU但不同刻字内容必须保留两行
  • 空刻字和未传刻字按照业务约定处理一致
  • 合并后优惠券、运费门槛和库存校验重新计算正确
判断是否“同一商品”不能只看SKU。先把两行的option完全展开,通常差异会非常直接。