Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

croftCoder's avatar

Merge guest cart with user cart

<?php

namespace App\Services;

use App\Models\Cart;
use App\Models\ProductVariant;
use App\Models\Coupon;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Exception;

class CartService
{
    protected $pricingService;

    public function __construct(PricingService $pricingService)
    {
        $this->pricingService = $pricingService;
    }

    public function getCart($userId = null)
    {
        if (Auth::check()) {
            // User is authenticated
            $userId = Auth::id();
            $cart = Cart::where('user_id', $userId)->firstOrCreate(['user_id' => $userId]);

            // Check and merge guest cart if exists
            $guestCartIdentifier = session('cart_identifier');
            if ($guestCartIdentifier) {
                $guestCart = Cart::where('cart_identifier', $guestCartIdentifier)->first();
                if ($guestCart) {
                    $this->mergeGuestCart($guestCart, $cart); // Merge guest cart items into user's cart
                    session()->forget('cart_identifier'); // Remove guest cart identifier from session
                    $guestCart->delete(); // Optionally, delete the guest cart
                }
            }

            return $cart;
        } else {
            // Guest user
            $cartIdentifier = session('cart_identifier') ?: Cookie::get('cart_identifier');

            if ($cartIdentifier) {
                $cart = Cart::where('cart_identifier', $cartIdentifier)->first();
                if (!$cart) {
                    // If no cart exists for the given identifier, create a new cart
                    $cart = Cart::create(['cart_identifier' => $cartIdentifier]);
                }
            } else {
                // If no cart identifier found in session or cookie, create a new cart and identifier
                $cart = Cart::create();
            }

            // Ensure the session and cookie are set with the cart identifier
            session(['cart_identifier' => $cart->cart_identifier]);
            Cookie::queue('cart_identifier', $cart->cart_identifier, 60 * 24 * 7); // 7 days

            return $cart;
        }
    }

    protected function mergeGuestCart($guestCart, $userCart)
    {
        // Ensure both carts are valid
        if (!$guestCart || !$userCart) {
            return;
        }

        foreach ($guestCart->items as $guestCartItem) {
            $existingItem = $userCart->items()->where('variant_id', $guestCartItem->variant_id)->first();

            if ($existingItem) {
                // Item exists in user's cart, merge quantities and update price
                $existingItem->quantity += $guestCartItem->quantity;
                $existingItem->price = $guestCartItem->price; // Update the price if needed
                $existingItem->save();
            } else {
                // Item does not exist in user's cart, move it
                $guestCartItem->cart_id = $userCart->id;
                $guestCartItem->save();
            }
        }

        // apply coupons from guest cart to user cart
        // #### THIS IS MY PROBLEM
        foreach ($guestCart->coupons as $coupon) {
            if (!$userCart->coupons->contains($coupon->id)) {
                $userCart->coupons()->attach($coupon->id);
            }
        }
        // #### END OF PROBLEM SECTION

        // Apply cart rules after merging
        // $this->updateCart($userCart); // DOING THIS DOESN'T WORK EITHER
        $this->applyCartRules($userCart);
    }

    public function addItem($cart, $variantId, $quantity)
    {
        $variant = ProductVariant::find($variantId);
        if (!$variant) {
            throw new Exception('Variant not found.');
        }

        if ($variant->stock->quantity < $quantity) {
            throw new Exception('Insufficient stock.');
        }

        $cartItem = $cart->items()->firstOrNew(['variant_id' => $variantId]);
        $cartItem->quantity += $quantity;
        $cartItem->variant_id = $variantId;
        $cartItem->price = $variant->price;
        $cartItem->save();

        $this->updateCart($cart);
    }

    public function removeItem($cart, $variantId)
    {
        $variant = ProductVariant::find($variantId);
        if (!$variant) {
            throw new Exception('Variant not found.');
        }

        $cart->items()->where('variant_id', $variantId)->delete();

        $this->updateCart($cart);
    }

    public function updateQuantity($cart, $variantId, $quantity)
    {
        $variant = ProductVariant::find($variantId);
        if (!$variant) {
            throw new Exception('Variant not found.');
        }

        $cartItem = $cart->items()->where('variant_id', $variantId)->first();
        if ($cartItem) {
            $cartItem->quantity = $quantity;
            $cartItem->price = $variant->price; // Update the price
            $cartItem->save();

            $this->updateCart($cart);
        }
    }

    public function applyCoupon(Cart $cart, $couponCode)
    {
        // Check if the coupon is already applied
        if ($this->pricingService->hasCoupon($cart, $couponCode)) {
            Session::flash('error', 'Coupon is already applied.');
            return false;
        }

        // Find the coupon by code
        $coupon = Coupon::where('code', $couponCode)->first();

        // Check if the coupon exists
        if (!$coupon) {
            Session::flash('error', 'Invalid coupon code.');
            return false;
        }

        // Check for minimum purchase amount
        if ($cart->subtotal < $coupon->couponable->minimum_purchase_amount) {
            Session::flash('error', 'Coupon cannot be applied. Minimum purchase amount not met.');
            return false;
        }

        // Apply the coupon to the cart
        $couponApplied = $coupon->couponable->apply($cart);
        if ($couponApplied) {
            // Attach the coupon to the cart if it was successfully applied
            $cart->coupons()->attach($coupon->id);
            $this->pricingService->applyCouponsToCart($cart); // Apply the coupon

            Session::flash('success', 'Coupon applied successfully.');
            return true;
        } else {
            Session::flash('error', 'Failed to apply the coupon.');
            return false;
        }
    }

    public function removeCoupon(Cart $cart, $couponCode)
    {
        // Find the coupon by code
        $coupon = Coupon::where('code', $couponCode)->first();

        // Check if the coupon exists
        if (!$coupon) {
            Session::flash('error', 'Invalid coupon code.');
            return false;
        }

        if ($coupon->couponable instanceof \App\Contracts\Applicable) {
            $coupon->couponable->remove($cart);
        }

        // Detach the coupon from the cart
        $cart->coupons()->detach($coupon->id);

        // Save the updated cart
        $cart->save();

        Session::flash('success', 'Coupon removed successfully.');
        return true;
    }

    protected function validateCoupons(Cart $cart)
    {
        $cartCoupons = $cart->coupons;
        foreach ($cartCoupons as $coupon) {
            if ($cart->subtotal < $coupon->couponable->minimum_purchase_amount) {
                $cart->coupons()->detach($coupon->id);
                if ($coupon->couponable instanceof \App\Contracts\Applicable) {
                    $coupon->couponable->remove($cart);
                }
                Session::flash('warning', 'Coupon removed due to insufficient cart value.');
            }
        }
    }

    public function applyCartRules($cart)
    {
        $cart->discount = 0;
        $cart->tax = 0;
        $cart->shipping_cost = 0;

        $this->validateCoupons($cart);

        // $pricingService = app(PricingService::class);
        $this->pricingService->applyDiscounts($cart);
        $this->pricingService->calculateTax($cart, config('cart.tax_rate'));
        $this->pricingService->calculateShipping($cart);

        $cart->save();
    }

    public function updateCart($cart)
    {
        $cart->discount = 0;
        $cart->tax = 0;
        $cart->shipping_cost = 0;

        $this->validateCoupons($cart);

        // $pricingService = app(PricingService::class);
        $this->pricingService->calculateTax($cart, config('cart.tax_rate'));
        $this->pricingService->calculateShipping($cart);

        $this->pricingService->updateCartCoupons($cart);

        $cart->save();
    }
}

trying to merge guest cart with user's cart and transfer coupons from the guest cart to the user's cart. everything works fine except when i add the code to transfer the coupons, the coupon gets attached to the user's cart but not calculated or stored and the guest cart doesn't get deleted either.

0 likes
0 replies

Please or to participate in this conversation.