validate([ 'purchasable_id' => ['required', 'integer'], 'quantity' => ['nullable', 'integer', 'min:1'], ]); $variant = ProductVariant::findOrFail($data['purchasable_id']); try { $this->cart->addLine($variant, $data['quantity'] ?? 1); } catch (CartException) { return $this->stockError($variant); } return view('checkout::partials.cart-body'); } public function updateLine(string $locale, Request $request, int $line): View|JsonResponse { $quantity = (int) $request->validate([ 'quantity' => ['required', 'integer', 'min:0'], ])['quantity']; try { $quantity === 0 ? $this->cart->removeLine($line) : $this->cart->updateLine($line, $quantity); } catch (CartException) { $variant = CartLine::find($line)?->purchasable; return $this->stockError($variant instanceof ProductVariant ? $variant : null); } return view('checkout::partials.cart-body'); } /** * getTotalInventory() is the same number canBeFulfilledAtQuantity() * checked against (stock, for a tracked in_stock variant) — telling the * shopper how many are actually left beats a generic "not enough stock" * they'd otherwise have to guess around by trial and error. */ private function stockError(?ProductVariant $variant): JsonResponse { $available = $variant?->getTotalInventory() ?? 0; return response()->json([ 'error' => trans_choice('storefront.product.add_to_cart_failed', $available, ['count' => $available]), ], 422); } public function remove(string $locale, int $line): View { $this->cart->removeLine($line); return view('checkout::partials.cart-body'); } /** * A bad code is a normal, expected outcome here (typo, expired code), not * an error state for the request — it re-renders the same cart-body * partial with $couponError set, rather than a 4xx/redirect, so the fetch * + swap in bbk-cart-controller stays the one code path for every cart * mutation. */ public function applyCoupon(string $locale, Request $request): View { $code = $request->validate([ 'code' => ['required', 'string'], ])['code']; $couponError = false; try { $this->cart->applyCoupon($code); } catch (InvalidCouponException) { $couponError = true; } return view('checkout::partials.cart-body', ['couponError' => $couponError]); } public function removeCoupon(string $locale): View { $this->cart->removeCoupon(); return view('checkout::partials.cart-body'); } }