# Laravel Shopping Cart Laravel Shopping Cart is a high-performance e-commerce package for Laravel 11/12 that provides a complete shopping cart solution with advanced features like tax calculation, discount management, coupon systems, and flexible storage options. Built with modern PHP 8.2+ features, it offers developers an intuitive API for managing shopping carts, wishlists, and comparison lists within their Laravel applications. The package is designed for production environments with performance optimizations including Cache::memo() integration for 99% fewer config lookups, smart memory management reducing usage by 99%, and database query optimizations that are 87% faster than traditional implementations. It supports both session-based and database-driven storage, handles 10,000+ concurrent users efficiently, and provides a complete feature set including the Buyable trait for seamless model integration, multiple cart instances for different use cases, and comprehensive currency formatting helpers. ## Installation and Setup Install via Composer and configure storage ```bash # Install the package composer require saeedvir/shopping-cart # Publish configuration file php artisan vendor:publish --tag=shopping-cart-config # For database storage, publish and run migrations php artisan vendor:publish --tag=shopping-cart-migrations php artisan migrate ``` ```php // config/shopping-cart.php return [ 'storage' => 'database', // or 'session' 'tax' => [ 'enabled' => true, 'default_rate' => 0.15, // 15% 'included_in_price' => false, ], 'currency' => [ 'code' => 'USD', 'symbol' => '$', 'decimals' => 2, ], 'expiration' => 10080, // 7 days in minutes 'limits' => [ 'max_items' => 100, 'max_quantity_per_item' => 999, ], ]; ``` ## Add Items to Cart Add products to cart with various methods ```php use Saeedvir\ShoppingCart\Facades\Cart; use App\Models\Product; // Add a product model with quantity $product = Product::find(1); Cart::add($product, 2); // Add 2 items // Add with custom attributes (size, color, etc.) Cart::add($product, 1, [ 'size' => 'Large', 'color' => 'Blue', 'custom_text' => 'Happy Birthday', ]); // Add manually with array Cart::add([ 'buyable_type' => Product::class, 'buyable_id' => 1, 'name' => 'Wireless Headphones', 'price' => 99.99, ], 1); // Add with custom tax rate Cart::add([ 'buyable_type' => Product::class, 'buyable_id' => 5, 'name' => 'Premium Product', 'price' => 199.99, 'tax_rate' => 0.20, // 20% tax for this item ], 2); // Response: Returns CartItem instance with generated ID // CartItem { id: "cart_item_abc123", quantity: 2, price: 99.99 } ``` ## Using the Buyable Trait Add cart functionality directly to your models ```php // app/Models/Product.php use Illuminate\Database\Eloquent\Model; use Saeedvir\ShoppingCart\Traits\Buyable; class Product extends Model { use Buyable; protected $fillable = ['name', 'price', 'stock']; } // Usage in controllers or views $product = Product::find(1); // Add product to cart $product->addToCart(2); // Add 2 items // Add with attributes $product->addToCart(1, [ 'size' => 'Medium', 'color' => 'Red', ]); // Check if product is in cart if ($product->inCart()) { echo "Already in cart"; } // Get cart item for this product $cartItem = $product->cartItem(); if ($cartItem) { echo "Quantity: {$cartItem->quantity}"; echo "Subtotal: " . $cartItem->formattedSubtotal(); } // Remove from cart $product->removeFromCart(); // Works with different instances if ($product->inCart('wishlist')) { echo "In wishlist"; } ``` ## Retrieve and Display Cart Items Get cart items and display information ```php use Saeedvir\ShoppingCart\Facades\Cart; // Get all cart items $items = Cart::items(); foreach ($items as $item) { echo $item->name; // Product name echo $item->quantity; // Quantity echo $item->price; // Unit price echo $item->getSubtotal(); // Price × Quantity echo $item->getTax(); // Tax amount echo $item->getTotal(); // Total with tax // Access custom attributes $size = $item->attributes['size'] ?? 'N/A'; $color = $item->attributes['color'] ?? 'N/A'; // Formatted currency values echo $item->formattedPrice(); // "$99.99" echo $item->formattedSubtotal(); // "$199.98" echo $item->formattedTotal(); // "$229.98" } // Get specific item by ID $item = Cart::get($itemId); if ($item) { echo $item->name; } // Get total item count (sum of quantities) $count = Cart::count(); // Returns 5 if you have 2+3 items // Check if cart is empty if (Cart::isEmpty()) { echo "Your cart is empty"; } else { echo "You have {$count} items in cart"; } ``` ## Update Cart Items Modify quantities, prices, or attributes ```php use Saeedvir\ShoppingCart\Facades\Cart; // Update quantity only Cart::update($itemId, [ 'quantity' => 5, ]); // Update price (e.g., for dynamic pricing) Cart::update($itemId, [ 'price' => 89.99, ]); // Update attributes Cart::update($itemId, [ 'attributes' => [ 'size' => 'Large', 'color' => 'Green', ], ]); // Update multiple fields at once Cart::update($itemId, [ 'quantity' => 3, 'price' => 79.99, 'attributes' => [ 'size' => 'Medium', 'gift_wrap' => true, ], ]); // Returns updated CartItem instance or null if not found $updatedItem = Cart::update($itemId, ['quantity' => 10]); if ($updatedItem) { echo "Updated successfully"; } ``` ## Remove Items and Clear Cart Delete items or clear entire cart ```php use Saeedvir\ShoppingCart\Facades\Cart; // Remove specific item Cart::remove($itemId); // Remove using Buyable trait $product = Product::find(1); $product->removeFromCart(); // Clear all items but keep cart metadata and conditions Cart::clear(); // Completely destroy cart (removes from storage) Cart::destroy(); // In a controller with validation public function remove($itemId) { $item = Cart::get($itemId); if (!$item) { return back()->with('error', 'Item not found'); } Cart::remove($itemId); return back()->with('success', 'Item removed from cart'); } ``` ## Tax Calculation Automatic tax calculation based on configuration ```php use Saeedvir\ShoppingCart\Facades\Cart; // Get cart totals $subtotal = Cart::subtotal(); // Before tax: 249.99 $tax = Cart::tax(); // Tax amount: 37.50 (15% of subtotal) $total = Cart::total(); // Final total: 287.49 // Get formatted amounts with currency echo Cart::formattedSubtotal(); // "$249.99" echo Cart::formattedTax(); // "$37.50" echo Cart::formattedTotal(); // "$287.49" // Per-item tax information foreach (Cart::items() as $item) { echo "Item: {$item->name}"; echo "Subtotal: " . $item->formattedSubtotal(); // "$99.98" echo "Tax: " . $item->formattedTax(); // "$15.00" echo "Total: " . $item->formattedTotal(); // "$114.98" } // Add item with custom tax rate Cart::add([ 'buyable_type' => Product::class, 'buyable_id' => 10, 'name' => 'Luxury Item', 'price' => 500.00, 'tax_rate' => 0.25, // 25% tax rate ], 1); // Tax calculation respects configuration // If tax is included in price, it's extracted // If not included, it's added to the total ``` ## Apply Discounts and Conditions Add discounts, fees, and other conditions ```php use Saeedvir\ShoppingCart\Facades\Cart; // Apply percentage discount Cart::condition('holiday_sale', 'discount', 20, 'percentage'); // Apply fixed amount discount Cart::condition('new_customer', 'discount', 10.00, 'fixed'); // Apply shipping fee Cart::condition('shipping', 'fee', 5.99, 'fixed'); // Apply handling fee Cart::condition('handling', 'fee', 2.50, 'fixed'); // Get discount amount $discount = Cart::discount(); // Total discount amount // Get final total (includes all conditions) $total = Cart::total(); // Subtotal + Tax - Discount + Fees // Remove a condition Cart::removeCondition('shipping'); // Conditional logic for free shipping if (Cart::subtotal() >= 50) { Cart::removeCondition('shipping'); } else { Cart::condition('shipping', 'fee', 9.99, 'fixed'); } // Bulk discount example if (Cart::count() >= 10) { Cart::condition('bulk_discount', 'discount', 15, 'percentage'); } // Example: Complete pricing display echo "Subtotal: " . Cart::formattedSubtotal(); // "$249.99" echo "Tax (15%): " . Cart::formattedTax(); // "$37.50" echo "Discount: -" . Cart::formattedDiscount(); // "$50.00" echo "Shipping: $5.99"; echo "Total: " . Cart::formattedTotal(); // "$243.48" ``` ## Coupon System Apply and validate coupon codes ```php use Saeedvir\ShoppingCart\Facades\Cart; use Saeedvir\ShoppingCart\Exceptions\InvalidCouponException; use App\Models\Coupon; // Apply coupon with validation callback try { Cart::applyCoupon('SAVE20', function ($code, $cart) { // Fetch coupon from database $coupon = Coupon::where('code', $code) ->where('is_active', true) ->where('starts_at', '<=', now()) ->where('expires_at', '>=', now()) ->first(); // Validate coupon exists if (!$coupon) { return false; } // Check minimum purchase requirement if ($cart->subtotal() < $coupon->minimum_purchase) { return false; } // Check maximum usage if ($coupon->usage_count >= $coupon->max_uses) { return false; } // Apply discount based on coupon type if ($coupon->type === 'percentage') { $cart->condition('coupon', 'discount', $coupon->value, 'percentage'); } else { $cart->condition('coupon', 'discount', $coupon->value, 'fixed'); } // Increment usage count $coupon->increment('usage_count'); return true; }); echo "Coupon applied successfully!"; } catch (InvalidCouponException $e) { echo "Invalid or expired coupon code"; } // Remove coupon Cart::removeCondition('coupon'); // Check if coupon is applied $metadata = Cart::getMetadata(); if (isset($metadata['coupon'])) { echo "Coupon code: {$metadata['coupon']}"; } ``` ## Multiple Cart Instances Support for cart, wishlist, compare, and custom instances ```php use Saeedvir\ShoppingCart\Facades\Cart; // Default shopping cart Cart::instance('default')->add($product, 2); // Or without instance (uses 'default') Cart::add($product, 2); // Wishlist instance Cart::instance('wishlist')->add($product, 1); $wishlistItems = Cart::instance('wishlist')->items(); $wishlistCount = Cart::instance('wishlist')->count(); // Compare list instance Cart::instance('compare')->add($product1, 1); Cart::instance('compare')->add($product2, 1); $compareItems = Cart::instance('compare')->items(); // Custom instance for saved items Cart::instance('saved-for-later')->add($product, 1); // Each instance has separate items and totals echo "Cart total: " . Cart::instance('default')->formattedTotal(); echo "Wishlist count: " . Cart::instance('wishlist')->count(); // Move item from wishlist to cart $item = Cart::instance('wishlist')->get($itemId); if ($item) { Cart::instance('default')->add([ 'buyable_type' => $item->buyableType, 'buyable_id' => $item->buyableId, 'name' => $item->name, 'price' => $item->price, ], $item->quantity); Cart::instance('wishlist')->remove($itemId); } // Clear specific instance Cart::instance('wishlist')->clear(); // With database storage, each instance is stored separately // Allows same user to have cart, wishlist, and compare list // Uses composite unique key: identifier + instance ``` ## Cart Metadata Store additional information with the cart ```php use Saeedvir\ShoppingCart\Facades\Cart; // Set individual metadata Cart::setMetadata('gift_message', 'Happy Birthday!'); Cart::setMetadata('gift_wrap', true); Cart::setMetadata('delivery_date', '2025-12-25'); Cart::setMetadata('shipping_method', 'express'); Cart::setMetadata('customer_note', 'Please ring doorbell'); // Get specific metadata $message = Cart::getMetadata('gift_message'); // "Happy Birthday!" $giftWrap = Cart::getMetadata('gift_wrap'); // true // Get all metadata $allMetadata = Cart::getMetadata(); /* [ 'gift_message' => 'Happy Birthday!', 'gift_wrap' => true, 'delivery_date' => '2025-12-25', 'shipping_method' => 'express', 'customer_note' => 'Please ring doorbell', ] */ // Use metadata in checkout public function checkout(Request $request) { if ($request->has('gift_message')) { Cart::setMetadata('gift_message', $request->gift_message); } if ($request->gift_wrap) { Cart::setMetadata('gift_wrap', true); Cart::condition('gift_wrap_fee', 'fee', 4.99, 'fixed'); } // Process order with metadata $cartData = Cart::toArray(); $order = Order::create([ 'total' => $cartData['total'], 'metadata' => $cartData['metadata'], ]); } ``` ## Identifier Helper Methods Extract user ID or session ID from cart identifiers ```php use Saeedvir\ShoppingCart\Facades\Cart; // Get user ID from identifier (if pattern is "user_") $userId = Cart::getUserId(); // Example: identifier "user_123" returns 123 // Returns null if not a user pattern // Get session ID from identifier (if NOT a user pattern) $sessionId = Cart::getUserSessionId(); // Example: identifier "session_abc123" returns "session_abc123" // Returns null if it's a user pattern // Practical usage in analytics if ($userId = Cart::getUserId()) { // This is a logged-in user's cart $user = User::find($userId); logger("Cart activity for user: {$user->email}"); // Track user behavior Analytics::track('cart_activity', [ 'user_id' => $userId, 'cart_total' => Cart::total(), ]); } elseif ($sessionId = Cart::getUserSessionId()) { // This is a guest/session cart logger("Guest cart: {$sessionId}"); // Prompt user to login session()->flash('message', 'Login to save your cart'); } // Merge guest cart with user cart on login public function mergeCartOnLogin($userId) { $guestCart = Cart::instance('default'); $guestItems = $guestCart->items(); // Switch to user cart $userIdentifier = "user_{$userId}"; $userCart = new Cart(app(CartStorageInterface::class), $userIdentifier); // Transfer items foreach ($guestItems as $item) { $userCart->add([ 'buyable_type' => $item->buyableType, 'buyable_id' => $item->buyableId, 'name' => $item->name, 'price' => $item->price, ], $item->quantity, $item->attributes); } // Clear guest cart $guestCart->destroy(); } ``` ## Currency Formatting Format prices with currency symbols ```php use Saeedvir\ShoppingCart\Facades\Cart; // Cart-level formatted amounts echo Cart::formattedSubtotal(); // "$249.99" echo Cart::formattedTax(); // "$37.50" echo Cart::formattedDiscount(); // "$25.00" echo Cart::formattedTotal(); // "$262.49" // Item-level formatted amounts $item = Cart::items()->first(); echo $item->formattedPrice(); // "$99.99" echo $item->formattedSubtotal(); // "$199.98" echo $item->formattedTax(); // "$30.00" echo $item->formattedTotal(); // "$229.98" // Using global helper functions echo cart_currency(99.99); // "$99.99" echo cart_currency(1234.56); // "$1,234.56" echo cart_currency_symbol(); // "$" echo cart_currency_code(); // "USD" // Format without symbol echo cart_currency(99.99, false); // "99.99" // Currency configuration in config/shopping-cart.php 'currency' => [ 'code' => 'USD', 'symbol' => '$', 'decimals' => 2, 'decimal_separator' => '.', 'thousand_separator' => ',', ], // Display in Blade templates @foreach(Cart::items() as $item)
{{ $item->buyable->description }}
Stock: {{ $item->buyable->stock }}
Price: {{ $item->formattedPrice() }}