Przeglądaj źródła

赠品修改主商品的时候报错问题

llp 1 tydzień temu
rodzic
commit
392bb6e2d9

+ 98 - 9
packages/Longyi/FreeGift/src/Services/GiftProcessor.php

@@ -197,7 +197,7 @@ class GiftProcessor
 
             // 规则已不再成立 → 把赠品从购物车里摘掉
             if ($gift->getAllowedQty() < 1) {
-                $this->cartItemRepository->delete($cartItem->id);
+                $this->deleteCartItemRow($cart, $cartItem->id);
 
                 $gift->setCartItemId(null)->setReservedQty(0);
 
@@ -208,12 +208,19 @@ class GiftProcessor
             if ($gift->getAllowedQty() < $gift->getReservedQty()) {
                 $qty = $gift->getAllowedQty();
 
-                $cartItem = $this->cartItemRepository->update([
+                $cartItem = $this->updateCartItemRow($cartItem->id, [
                     'quantity'   => $qty,
                     'total'      => (float) $cartItem->base_price * $qty,
                     'base_total' => (float) $cartItem->base_price * $qty,
                     'additional' => array_merge($cartItem->additional ?? [], ['quantity' => $qty]),
-                ], $cartItem->id);
+                ]);
+
+                // 行已被本轮其它步骤删掉(快照失效)→ 作废指针,交给后续流程重建
+                if (! $cartItem) {
+                    $gift->setCartItemId(null)->setReservedQty(0);
+
+                    continue;
+                }
 
                 $gift->setReservedQty($qty);
             }
@@ -270,18 +277,18 @@ class GiftProcessor
                 if (! $cartItem) {
                     $gift->setReservedQty(max(0, $gift->getReservedQty() - $excess));
                 } elseif ((float) $cartItem->quantity - $excess < 1) {
-                    $this->cartItemRepository->delete($cartItem->id);
+                    $this->deleteCartItemRow($cart, $cartItem->id);
 
                     $gift->setCartItemId(null)->setReservedQty(0);
                 } else {
                     $qty = (float) $cartItem->quantity - $excess;
 
-                    $this->cartItemRepository->update([
+                    $this->updateCartItemRow($cartItem->id, [
                         'quantity'   => $qty,
                         'total'      => (float) $cartItem->base_price * $qty,
                         'base_total' => (float) $cartItem->base_price * $qty,
                         'additional' => array_merge($cartItem->additional ?? [], ['quantity' => $qty]),
-                    ], $cartItem->id);
+                    ]);
 
                     $gift->setReservedQty($qty);
                 }
@@ -615,7 +622,7 @@ class GiftProcessor
 
         $qty = (float) $cartItem->quantity;
 
-        $this->cartItemRepository->update([
+        $this->updateCartItemRow($cartItem->id, [
             'price'               => $price,
             'price_incl_tax'      => $price,
             'base_price'          => $price,
@@ -624,7 +631,89 @@ class GiftProcessor
             'total_incl_tax'      => $price * $qty,
             'base_total'          => $price * $qty,
             'base_total_incl_tax' => $price * $qty,
-        ], $cartItem->id);
+        ]);
+    }
+
+    /**
+     * 安全更新购物车行。
+     *
+     * findGiftCartItem() 认领的是 $cart->all_items 这份「本轮快照」,而 Repository
+     * 的 delete() 不会刷新该关联:同一轮里先删后写的场景(syncGiftItems 删行之后,
+     * shrinkOneOfGroups / applyGiftPrice 又用旧快照认领到同一行)会让 update() 打到
+     * 已删除的 id —— Prettus 的 update() 内部是 findOrFail(),于是抛
+     * ModelNotFoundException(No query results for model [CartItem] xx)。这里先确认
+     * 行仍存在,悬空则跳过并返回 null。
+     *
+     * @param  int|string  $cartItemId
+     * @return \Webkul\Checkout\Contracts\CartItem|null
+     */
+    protected function updateCartItemRow($cartItemId, array $payload)
+    {
+        if (! $cartItemId || ! $this->cartItemRepository->find($cartItemId)) {
+            return null;
+        }
+
+        return $this->cartItemRepository->update($payload, $cartItemId);
+    }
+
+    /**
+     * 安全删除购物车行,并把该行从 $cart 的内存关联里剔除。
+     *
+     * Prettus 的 delete() 内部会 clone 查询结果,行不存在时 clone null 会致命报错,
+     * 所以先确认存在。
+     *
+     * 更要紧的是「删完同步快照」:findGiftCartItem() / removeOrphanGiftRows() 认领的
+     * 都是 $cart->all_items 这份本轮快照,而 Repository 的删除不会刷新它。同一轮里
+     * 删完之后立刻还会再扫一遍(removeOrphanGiftRows),旧快照仍能看到已删的行,
+     * 于是又删/又写一次 —— delete 抛 `__clone method called on non-object`,
+     * update 抛 ModelNotFoundException。删完立刻把行从快照里摘掉,后续扫描就看不到它。
+     *
+     * @param  \Webkul\Checkout\Contracts\Cart  $cart
+     * @param  int|string  $cartItemId
+     */
+    protected function deleteCartItemRow($cart, $cartItemId): void
+    {
+        if (! $cartItemId) {
+            return;
+        }
+
+        if ($this->cartItemRepository->find($cartItemId)) {
+            $this->cartItemRepository->delete($cartItemId);
+        }
+
+        $this->forgetCartItemFromSnapshot($cart, $cartItemId);
+    }
+
+    /**
+     * 把某一行从 $cart 已加载的 items / all_items 关联里剔除。
+     *
+     * 只动内存里已经加载的关系(不触发重新查询);没加载过的关系不动,
+     * 它下次被访问时会回库拿最新数据。
+     *
+     * @param  \Webkul\Checkout\Contracts\Cart  $cart
+     * @param  int|string  $cartItemId
+     */
+    protected function forgetCartItemFromSnapshot($cart, $cartItemId): void
+    {
+        if (! $cart || ! $cartItemId) {
+            return;
+        }
+
+        foreach (['items', 'all_items'] as $relation) {
+            if (! $cart->relationLoaded($relation)) {
+                continue;
+            }
+
+            $items = $cart->getRelation($relation);
+
+            if (! $items instanceof \Illuminate\Support\Collection) {
+                continue;
+            }
+
+            $cart->setRelation($relation, $items
+                ->reject(fn ($item) => (int) $item->id === (int) $cartItemId)
+                ->values());
+        }
     }
 
     /**
@@ -655,7 +744,7 @@ class GiftProcessor
                 continue;
             }
 
-            $this->cartItemRepository->delete($cartItem->id);
+            $this->deleteCartItemRow($cart, $cartItem->id);
         }
     }