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

Magento 2:自动移除购物车中缺货商品方案(含登录用户与访客)

解决Magento 2购物车中缺货商品自动移除的方案

Hey there! I've dealt with this exact scenario before—users (both logged-in and guests) having out-of-stock items lingering in their carts for months. Let's break down the solutions that cover simple, configurable products, and both user types.


1. 自动移除:自定义模块+事件/定时任务

This is the most robust way to handle it automatically, so you don't have to manually clean carts.

1.1 利用购物车加载事件实时移除

You can hook into the checkout_cart_load_after event to check and remove out-of-stock items whenever a cart is loaded (works for both guests and logged-in users):

Step 1: 创建事件观察者

在你的自定义模块的 etc/events.xml 文件中添加:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="checkout_cart_load_after">
        <observer name="remove_out_of_stock_cart_items" instance="Vendor\Module\Observer\RemoveOutOfStockItems" />
    </event>
</config>

Step 2: 实现观察者逻辑

创建 Vendor/Module/Observer/RemoveOutOfStockItems.php 文件:

<?php
namespace Vendor\Module\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\CatalogInventory\Model\Stock\StockItemRepository;
use Magento\Checkout\Model\Cart;
use Magento\Framework\Message\ManagerInterface;

class RemoveOutOfStockItems implements ObserverInterface
{
    protected $stockItemRepository;
    protected $cart;
    protected $messageManager;

    public function __construct(
        StockItemRepository $stockItemRepository,
        Cart $cart,
        ManagerInterface $messageManager
    ) {
        $this->stockItemRepository = $stockItemRepository;
        $this->cart = $cart;
        $this->messageManager = $messageManager;
    }

    public function execute(Observer $observer)
    {
        $cartItems = $this->cart->getItems();
        $itemsToRemove = [];

        foreach ($cartItems as $item) {
            // 处理简单商品
            if ($item->getProductType() == 'simple') {
                $stockItem = $this->stockItemRepository->get($item->getProductId());
                if (!$stockItem->getIsInStock()) {
                    $itemsToRemove[] = $item->getId();
                }
            }
            // 处理可配置商品(检查所有子商品库存)
            elseif ($item->getProductType() == 'configurable') {
                $hasInStockChild = false;
                $childItems = $item->getChildrenItems();
                foreach ($childItems as $child) {
                    $stockItem = $this->stockItemRepository->get($child->getProductId());
                    if ($stockItem->getIsInStock()) {
                        $hasInStockChild = true;
                        break;
                    }
                }
                // 所有子商品都缺货时,标记父商品移除
                if (!$hasInStockChild) {
                    $itemsToRemove[] = $item->getId();
                }
            }
        }

        // 执行移除操作
        foreach ($itemsToRemove as $itemId) {
            $this->cart->removeItem($itemId)->save();
        }

        // 给用户提示(可选)
        if (!empty($itemsToRemove)) {
            $this->messageManager->addNoticeMessage('部分缺货商品已从您的购物车中移除。');
        }

        return $this;
    }
}

这个观察者会在用户访问购物车页面、进入结算流程等场景触发,自动移除缺货的简单商品,以及所有子商品都缺货的可配置商品,覆盖访客和登录用户两种场景。

1.2 定时任务(Cron Job)批量清理旧购物车

如果想清理数月未访问的旧购物车,避免每次加载购物车都执行检查,可以设置定时任务:

Step 1: 添加Cron配置

在模块的 etc/crontab.xml 文件中添加:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
    <group id="default">
        <job name="remove_out_of_stock_cart_items_cron" instance="Vendor\Module\Cron\RemoveOutOfStockItems" method="execute">
            <schedule>0 2 * * *</schedule> <!-- 每天凌晨2点执行 -->
        </job>
    </group>
</config>

Step 2: 实现Cron逻辑

创建 Vendor/Module/Cron/RemoveOutOfStockItems.php 文件:

<?php
namespace Vendor\Module\Cron;

use Magento\Checkout\Model\ResourceModel\Cart\CollectionFactory as CartCollectionFactory;
use Magento\CatalogInventory\Model\Stock\StockItemRepository;
use Magento\Checkout\Model\Cart;
use Magento\Framework\App\ObjectManager;

class RemoveOutOfStockItems
{
    protected $cartCollectionFactory;
    protected $stockItemRepository;

    public function __construct(
        CartCollectionFactory $cartCollectionFactory,
        StockItemRepository $stockItemRepository
    ) {
        $this->cartCollectionFactory = $cartCollectionFactory;
        $this->stockItemRepository = $stockItemRepository;
    }

    public function execute()
    {
        // 获取3个月未更新的购物车(可自行调整时间)
        $threeMonthsAgo = strtotime('-3 months');
        $cartCollection = $this->cartCollectionFactory->create();
        $cartCollection->addFieldToFilter('updated_at', ['lt' => date('Y-m-d H:i:s', $threeMonthsAgo)]);

        foreach ($cartCollection as $cart) {
            $cartModel = ObjectManager::getInstance()->create(Cart::class)->setQuote($cart);
            $cartItems = $cartModel->getItems();
            $itemsToRemove = [];

            // 复用观察者中的商品库存检查逻辑
            foreach ($cartItems as $item) {
                if ($item->getProductType() == 'simple') {
                    $stockItem = $this->stockItemRepository->get($item->getProductId());
                    if (!$stockItem->getIsInStock()) {
                        $itemsToRemove[] = $item->getId();
                    }
                } elseif ($item->getProductType() == 'configurable') {
                    $hasInStockChild = false;
                    $childItems = $item->getChildrenItems();
                    foreach ($childItems as $child) {
                        $stockItem = $this->stockItemRepository->get($child->getProductId());
                        if ($stockItem->getIsInStock()) {
                            $hasInStockChild = true;
                            break;
                        }
                    }
                    if (!$hasInStockChild) {
                        $itemsToRemove[] = $item->getId();
                    }
                }
            }

            // 移除标记的商品
            foreach ($itemsToRemove as $itemId) {
                $cartModel->removeItem($itemId)->save();
            }
        }
    }
}

2. 手动移除:针对特定用户/访客

如果需要手动处理个别场景:

针对登录用户

  1. 进入后台 Customers > All Customers,找到目标用户并点击编辑
  2. 在左侧菜单选择Shopping Cart,即可看到该用户购物车中的所有商品
  3. 点击缺货商品旁的垃圾桶图标即可移除

针对访客用户

访客购物车存储在quote表中(customer_id为NULL且is_active为1):

  • 先通过SQL查询定位目标购物车和缺货商品(操作前请备份数据库):
-- 查询访客购物车中的缺货商品
SELECT q.entity_id AS quote_id, qi.item_id, qi.sku 
FROM quote q
JOIN quote_item qi ON q.entity_id = qi.quote_id
JOIN cataloginventory_stock_item si ON qi.product_id = si.product_id
WHERE q.customer_id IS NULL AND si.is_in_stock = 0;
  • 执行删除操作:
DELETE FROM quote_item WHERE quote_id = '目标购物车ID' AND item_id = '要移除的商品ID';
  • 或者使用CLI命令(已知购物车ID和商品ID时):
bin/magento quote:item:remove 目标购物车ID 要移除的商品ID

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:17:46