A comprehensive shopping cart package for Laravel 11/12 with tax calculation, discounts, coupons, and flexible storage options
saeedvir/shopping-cart is a Laravel package for a comprehensive shopping cart package for laravel 11/12 with tax calculation, discounts, coupons, and flexible storage options.
It currently has 2 GitHub stars and 16 downloads on Packagist (latest version v1.1.0).
Install it with composer require saeedvir/shopping-cart.
Discover more Laravel packages by saeedvir
or browse all Laravel packages to compare alternatives.
Last updated
A high-performance shopping cart package for Laravel 11/12 with tax calculation, discounts, coupons, and flexible storage options.
Install the package via Composer:
composer require saeedvir/shopping-cart
Publish the configuration file:
php artisan vendor:publish --tag=shopping-cart-config
If using database storage, publish and run the migrations:
php artisan vendor:publish --tag=shopping-cart-migrations
php artisan migrate
Note: The package includes performance optimizations with database indexes. Make sure to run migrations to benefit from optimal query performance.
Cache::memo() for configuration caching, resulting in 99% fewer config lookupsfirstOrCreate to updateOrCreate for better conflict handlingidentifier + instance combination allows multiple cart types per userThe configuration file is located at config/shopping-cart.php. You can configure:
return [
'storage' => 'session', // or 'database'
'tax' => [
'enabled' => true,
'default_rate' => 0.15, // 15%
'included_in_price' => false,
],
'currency' => [
'code' => 'USD',
'symbol' => '$',
],
];
use Saeedvir\ShoppingCart\Facades\Cart;
// Add a product model
$product = Product::find(1);
Cart::add($product, 2); // Add 2 items
// Add with custom attributes
Cart::add($product, 1, [
'size' => 'Large',
'color' => 'Red',
]);
// Add manually
Cart::add([
'buyable_type' => Product::class,
'buyable_id' => 1,
'name' => 'Product Name',
'price' => 99.99,
], 1);
Add the Buyable trait to your product model:
use Saeedvir\ShoppingCart\Traits\Buyable;
class Product extends Model
{
use Buyable;
}
// Now you can use convenient methods
$product->addToCart(2);
$product->inCart(); // Returns true/false
$product->removeFromCart();
// Get all items
$items = Cart::items();
// Get item count
$count = Cart::count();
// Check if cart is empty
if (Cart::isEmpty()) {
// Cart is empty
}
// Get a specific item
$item = Cart::get($itemId);
Cart::update($itemId, [
'quantity' => 3,
'price' => 89.99,
'attributes' => ['color' => 'Blue'],
]);
// Remove a specific item
Cart::remove($itemId);
// Clear entire cart
Cart::clear();
// Destroy cart (remove from storage)
Cart::destroy();
Support different cart types for the same user (cart, wishlist, compare, saved items):
// Default shopping cart
Cart::instance('default')->add($product, 2);
// Wishlist
Cart::instance('wishlist')->add($product, 1);
// Compare list
Cart::instance('compare')->add($anotherProduct, 1);
// Custom instance
Cart::instance('saved-for-later')->add($product, 1);
// Each instance maintains separate items, totals, and conditions
$cartItems = Cart::instance('default')->items();
$wishlistItems = Cart::instance('wishlist')->items();
Database Optimization: With database storage, each instance is stored separately with a composite unique key (identifier + instance), allowing the same user to have multiple cart types without conflicts.
Taxes are automatically calculated based on configuration:
// Get cart totals
$subtotal = Cart::subtotal(); // Before tax
$tax = Cart::tax(); // Tax amount
$total = Cart::total(); // Final total
// Per-item tax
foreach (Cart::items() as $item) {
echo $item->getTax();
echo $item->getTotal();
}
Apply discounts, fees, and other conditions:
// Apply percentage discount
Cart::condition('sale', 'discount', 10, 'percentage');
// Apply fixed discount
Cart::condition('coupon', 'discount', 5.00, 'fixed');
// Apply fee
Cart::condition('handling', 'fee', 2.50, 'fixed');
// Remove condition
Cart::removeCondition('sale');
// Apply coupon with validation
Cart::applyCoupon('SAVE20', function ($code, $cart) {
// Validate coupon
$coupon = Coupon::where('code', $code)->first();
if (!$coupon || $coupon->isExpired()) {
return false;
}
// Apply discount
$cart->condition('coupon', 'discount', $coupon->value, 'percentage');
return true;
});
Store additional cart information:
// Set metadata
Cart::setMetadata('note', 'Gift wrapping requested');
Cart::setMetadata('shipping_method', 'express');
// Get metadata
$note = Cart::getMetadata('note');
$allMetadata = Cart::getMetadata();
Extract user ID or session ID from cart identifiers:
// 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
if ($userId = Cart::getUserId()) {
// This is a logged-in user's cart
$user = User::find($userId);
} elseif ($sessionId = Cart::getUserSessionId()) {
// This is a guest/session cart
logger("Guest cart: {$sessionId}");
}
Get formatted prices with currency symbols:
// 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 helper functions
echo cart_currency(99.99); // "$99.99"
echo cart_currency_symbol(); // "$"
echo cart_currency_code(); // "USD"
To avoid N+1 queries when displaying products:
// Load all product data at once (no N+1 queries!)
$cart = Cart::instance('default');
$cart->loadBuyables();
// Now safely access product details
foreach ($cart->items() as $item) {
echo $item->buyable->name;
echo $item->buyable->description;
// No additional queries!
}
// Get complete cart data
$summary = Cart::toArray();
/*
Returns:
[
'identifier' => 'session_abc123',
'instance' => 'default',
'items' => [...],
'count' => 5,
'subtotal' => 249.99,
'tax' => 37.50,
'discount' => 25.00,
'total' => 262.49,
'conditions' => [...],
'metadata' => [...],
]
*/
Cart data is stored in the user's session:
'storage' => 'session',
Cart data is persisted to the database:
'storage' => 'database',
Database storage provides:
The package is highly optimized for production use with real-world performance improvements:
Cache::memo() integration eliminates repeated configuration readsidentifier, instance, and foreign keys// Example: 1000 items in cart
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
Cart::add($product, 1);
}
$time = microtime(true) - $start;
// Completes in < 2 seconds with database storage
See the developer-docs/ folder in the package for detailed performance documentation and benchmarks.
The package fires events for cart operations (when enabled in config):
CartItemAdded (planned)CartItemUpdated (planned)CartItemRemoved (planned)CartCleared (planned)CartDestroyed (planned)| Method | Description |
|--------|-------------|
| instance(string $instance) | Set the cart instance |
| add($buyable, int $quantity, array $attributes) | Add item to cart |
| update(string $itemId, array $data) | Update cart item |
| remove(string $itemId) | Remove item from cart |
| get(string $itemId) | Get specific item |
| items() | Get all items |
| count() | Get total item count |
| isEmpty() | Check if cart is empty |
| clear() | Clear cart contents |
| destroy() | Destroy cart |
| condition(...) | Apply condition |
| applyCoupon(string $code, callable $validator) | Apply coupon |
| removeCondition(string $name) | Remove condition |
| subtotal() | Get subtotal |
| tax() | Get tax total |
| discount() | Get discount total |
| total() | Get final total |
| setMetadata(string $key, $value) | Set metadata |
| getMetadata(string $key) | Get metadata |
| getUserId() | Get user ID from identifier (if pattern is "user_") |
| getUserSessionId() | Get session ID from identifier (if NOT a user pattern) |
| Method | Description |
|--------|-------------|
| getSubtotal() | Get item subtotal |
| getTax() | Get item tax |
| getTotal() | Get item total |
| toArray() | Convert to array |
use Saeedvir\ShoppingCart\Facades\Cart;
// Add items
$product = Product::find(1);
Cart::add($product, 2);
// Apply discount
Cart::condition('sale', 'discount', 20, 'percentage');
// Get totals
echo Cart::formattedTotal(); // "$159.99"
// Multiple instances
Cart::instance('wishlist')->add($product);
composer test
Test controllers and examples are included in the examples/ directory of the package.
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)The MIT License (MIT). Please see License File for more information.
Made with ❤️ for the Laravel community