hcart / laravel-multi-cart
This is my package laravel-multi-cart
Fund package maintenance!
HCart
Requires
- php: ^8.4 || ^8.3 || ^8.2
- illuminate/cache: ^10.0||^11.0||^12.0
- illuminate/contracts: ^10.0||^11.0||^12.0
- illuminate/database: ^10.0||^11.0||^12.0
- illuminate/filesystem: ^10.0||^11.0||^12.0
- illuminate/redis: ^10.0||^11.0||^12.0
- illuminate/session: ^10.0||^11.0||^12.0
- illuminate/support: ^10.0||^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^2.9||^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^10.0.0||^9.0.0||^8.22.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/extension-installer: ^1.3||^2.0
- phpstan/phpstan-deprecation-rules: ^1.1||^2.0
- phpstan/phpstan-phpunit: ^1.3||^2.0
- spatie/laravel-ray: ^1.35
This package is auto-updated.
Last update: 2025-06-23 13:37:09 UTC
README
A flexible, extensible shopping cart solution for Laravel applications that supports multiple cart instances with configurable storage providers. Built for Laravel v11+ and PHP 8.2+, it leverages modern Laravel features including polymorphic relationships and JSON configuration storage.
Table of Contents
- Features
- Requirements
- Installation
- Configuration
- Basic Usage
- User Integration
- Cartable Items
- Storage Providers
- Advanced Configuration
- Events
- Commands
- API Reference
- Examples
- Testing
- Changelog
- Contributing
- Security Vulnerabilities
- Credits
- License
Features
- Multiple Cart Instances: Create and manage multiple named cart instances for different purposes (shopping, wishlist, favorites, etc.)
- Configurable Storage Providers: Choose from session, cache, database, Redis, or file storage providers
- Polymorphic Relationships: Add any Eloquent model to carts with full relationship support
- JSON Configuration Storage: Flexible configuration with JSON/JSONB support for enhanced flexibility
- Soft Delete Support: Built-in soft delete functionality with
deleted_at
timestamps for data recovery - Type-Safe Implementation: Modern PHP 8.2+ features with strict typing and comprehensive interfaces
- Comprehensive Event System: Listen to cart creation, updates, deletions, and item changes
- Built-in Validation: Automatic validation and error handling with custom exceptions
- Trait Support: Easy integration with User models and cartable items using Laravel traits
- Automatic Cleanup: Scheduled cleanup of expired carts with configurable retention policies
- Custom Callbacks: Extensible callback system for item uniqueness, updates, and removals
- Provider Migration: Seamless migration between different storage providers
- Performance Optimized: Efficient caching strategies and optimized database queries
Requirements
- Laravel Framework: v11.0+
- PHP Version: 8.2+
- Database: MySQL 8.0+, PostgreSQL 13+, SQLite 3.35+
- Cache Drivers: Redis, Memcached, File, Database
- Session Drivers: File, Cookie, Database, APC, Memcached, Redis
Installation
Step 1: Install via Composer
composer require hcart/laravel-multi-cart
Step 2: Publish Configuration
php artisan vendor:publish --tag="laravel-multi-cart-config"
Step 3: Database Setup (Optional)
If you plan to use the database provider, publish and run the migrations:
php artisan cart:publish-migrations php artisan migrate
Step 4: Environment Configuration
Add these environment variables to your .env
file:
LARAVEL_MULTI_CART_PROVIDER=session LARAVEL_MULTI_CART_TAX_RATE=0.08 LARAVEL_MULTI_CART_CURRENCY=USD
Configuration
The configuration file config/laravel-multi-cart.php
allows extensive customization:
<?php return [ // Default storage provider 'default' => env('LARAVEL_MULTI_CART_PROVIDER', 'session'), // Custom models (you can extend these) 'models' => [ 'cart' => \HCart\LaravelMultiCart\Models\Cart::class, 'cart_item' => \HCart\LaravelMultiCart\Models\CartItem::class, ], // Configuration class 'config_class' => \HCart\LaravelMultiCart\Config\LaravelMultiCartConfig::class, // Behavioral callbacks 'callbacks' => [ // Custom item uniqueness logic 'unique_item' => null, // Callback when item is updated 'item_update' => null, // Callback when item is removed 'item_remove' => null, ], // Cart behavior settings 'prevent_duplicates' => true, 'tax_rate' => env('LARAVEL_MULTI_CART_TAX_RATE', 0.0), 'currency' => env('LARAVEL_MULTI_CART_CURRENCY', 'USD'), // Storage provider configurations 'providers' => [ 'session' => [ 'driver' => 'session', 'prefix' => 'laravel_multi_cart_' ], 'cache' => [ 'driver' => 'cache', 'store' => env('CACHE_DRIVER', 'file'), 'prefix' => 'laravel_multi_cart_', 'ttl' => 3600 ], 'database' => [ 'driver' => 'database', 'connection' => env('DB_CONNECTION', 'mysql'), 'soft_deletes' => true ], 'redis' => [ 'driver' => 'redis', 'connection' => env('REDIS_CONNECTION', 'default'), 'prefix' => 'laravel_multi_cart_', 'ttl' => 3600 ], 'file' => [ 'driver' => 'file', 'path' => storage_path('laravel_multi_cart'), 'ttl' => 3600 ] ], // Automatic cleanup settings 'cleanup' => [ 'enabled' => true, 'schedule' => 'daily', 'expire_after' => 168 // hours (7 days) ] ];
Basic Usage
Creating and Managing Carts
use HCart\LaravelMultiCart\Facades\LaravelMultiCart; // Get or create a cart $cart = LaravelMultiCart::cart('shopping'); // Create cart with specific provider $cart = LaravelMultiCart::cart('wishlist', 'database'); // Create cart with custom configuration $cart = LaravelMultiCart::create('premium', [ 'tax_rate' => 0.15, 'currency' => 'EUR' ], 'database'); // Check if cart exists if (LaravelMultiCart::exists('shopping')) { // Cart exists }
Adding Items to Cart
// Basic item addition $product = Product::find(1); $cart->add($product, 2); // Add 2 quantities // Add with custom price $cart->add($product, 1, [], 19.99); // Add with attributes $cart->add($product, 1, [ 'size' => 'large', 'color' => 'red', 'gift_wrap' => true ]); // Check if item exists in cart if ($cart->has($product)) { // Product is in cart } // Get quantity of specific item $quantity = $cart->quantity($product);
Cart Operations
// Get cart information $itemCount = $cart->count(); $subtotal = $cart->subtotal(); $tax = $cart->tax(); $total = $cart->total(); // Get all items $items = $cart->items(); // Get specific item $item = $cart->get($itemId); // Update item $cart->update($itemId, [ 'quantity' => 5, 'price' => 24.99, 'attributes' => ['color' => 'blue'] ]); // Remove item $cart->remove($itemId); // Clear all items $cart->clear(); // Clone cart $clonedCart = $cart->clone('shopping_backup'); // Clone to different provider $dbCart = $cart->clone('shopping_db', 'database'); // Convert cart to different provider $convertedCart = $cart->convertToProvider('redis'); // Delete cart $cart->delete();
User Integration
Adding HasCarts Trait
Add the HasCarts
trait to your User model for seamless user-cart integration:
<?php namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use HCart\LaravelMultiCart\Traits\HasCarts; class User extends Authenticatable { use HasCarts; // ... other model code }
User Cart Operations
$user = auth()->user(); // Create user-specific cart $cart = $user->createCart('shopping', ['currency' => 'EUR']); // Get user's cart $cart = $user->getCart('shopping'); // Get cart with specific provider $cart = $user->getCart('wishlist', 'database'); // Check if user has cart if ($user->hasCart('favorites')) { $favorites = $user->getCart('favorites'); } // Get all user cart names $cartNames = $user->getCartNames(); // Clone user's cart $clonedCart = $user->cloneCart('shopping', 'shopping_backup'); // Convert cart to different provider $convertedCart = $user->convertCartToProvider('shopping', 'database'); // Delete user cart $user->deleteCart('shopping'); // Get user's carts relationship $userCarts = $user->carts; // Returns Eloquent collection
Cartable Items
Adding Cartable Trait
Add the Cartable
trait to models that can be added to carts:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use HCart\LaravelMultiCart\Traits\Cartable; class Product extends Model { use Cartable; // Required method for cart integration public function getCartPrice(): float { return (float) $this->price; } // Optional: Custom cart name public function getCartName(): string { return $this->name; } // Optional: Additional attributes for cart public function getCartAttributes(): array { return [ 'sku' => $this->sku, 'weight' => $this->weight, ]; } }
Cartable Operations
$product = Product::find(1); // Check if product is in any cart if ($product->isInCart()) { // Product is in at least one cart } // Check if product is in specific cart if ($product->isInCart('wishlist')) { // Product is in wishlist } // Get total quantity across all carts $totalQuantity = $product->getCartQuantity(); // Get quantity in specific cart $wishlistQuantity = $product->getCartQuantity('wishlist'); // Remove from all carts $product->removeFromCart(); // Remove from specific cart $product->removeFromCart('shopping'); // Get cart items relationship $cartItems = $product->cartItems; // Returns Eloquent collection
Storage Providers
Session Provider
Best for guest users and temporary carts:
$cart = LaravelMultiCart::cart('guest_cart', 'session');
Database Provider
Best for persistent user carts with relationships:
$cart = LaravelMultiCart::cart('user_cart', 'database'); // Supports user associations $cart->forUser($user); // Get cart ID for relationships $cartId = $cart->getCartId();
Cache Provider
Best for high-performance scenarios:
$cart = LaravelMultiCart::cart('fast_cart', 'cache');
Redis Provider
Best for distributed applications:
$cart = LaravelMultiCart::cart('distributed_cart', 'redis');
File Provider
Best for simple applications without database/cache:
$cart = LaravelMultiCart::cart('file_cart', 'file');
Advanced Configuration
Custom Unique Item Callbacks
Define how items are considered unique in carts:
use HCart\LaravelMultiCart\Config\LaravelMultiCartConfig; $config = new LaravelMultiCartConfig([ 'callbacks' => [ 'unique_item' => function ($cartableId, $cartableType, $attributes) { // Include size and color in uniqueness $uniqueKey = $cartableId . $cartableType; if (isset($attributes['size'])) { $uniqueKey .= $attributes['size']; } if (isset($attributes['color'])) { $uniqueKey .= $attributes['color']; } return md5($uniqueKey); }, ], ]); // Apply configuration to specific cart $cart = LaravelMultiCart::cart('custom_cart')->withConfig($config); // Set global configuration LaravelMultiCart::setConfig($config);
Item Update and Remove Callbacks
$config = new LaravelMultiCartConfig([ 'callbacks' => [ 'item_update' => function ($cartItem, $oldData, $newData) { // Log item updates Log::info('Cart item updated', [ 'cart_id' => $cartItem['cart_id'] ?? null, 'item_id' => $cartItem['id'], 'old_data' => $oldData, 'new_data' => $newData ]); }, 'item_remove' => function ($cartItem) { // Log item removals Log::info('Cart item removed', [ 'cart_id' => $cartItem['cart_id'] ?? null, 'item_id' => $cartItem['id'], 'cartable_type' => $cartItem['cartable_type'], 'cartable_id' => $cartItem['cartable_id'], ]); }, ], ]);
Runtime Configuration
// Update configuration at runtime $cart->setConfig([ 'tax_rate' => 0.10, 'currency' => 'GBP' ]); // Get configuration $config = $cart->getConfig(); $taxRate = $cart->getCartConfig()->getTaxRate();
Events
The package dispatches several events that you can listen to:
Cart Events
// Cart created HCart\LaravelMultiCart\Events\CartCreated::class // Cart updated HCart\LaravelMultiCart\Events\CartUpdated::class // Cart deleted HCart\LaravelMultiCart\Events\CartDeleted::class
Item Events
// Item added to cart HCart\LaravelMultiCart\Events\ItemAdded::class // Item updated in cart HCart\LaravelMultiCart\Events\ItemUpdated::class // Item removed from cart HCart\LaravelMultiCart\Events\ItemRemoved::class
Event Listeners
Create event listeners to respond to cart events:
<?php namespace App\Listeners; use HCart\LaravelMultiCart\Events\ItemAdded; class LogItemAdded { public function handle(ItemAdded $event) { Log::info('Item added to cart', [ 'cart_name' => $event->cartName, 'cartable_type' => $event->cartableType, 'cartable_id' => $event->cartableId, 'quantity' => $event->quantity, 'price' => $event->price, ]); } }
Register in EventServiceProvider
:
protected $listen = [ \HCart\LaravelMultiCart\Events\ItemAdded::class => [ \App\Listeners\LogItemAdded::class, ], ];
Commands
Cleanup Expired Carts
php artisan cart:cleanup
Migrate Between Providers
# Migrate all carts from session to database php artisan cart:migrate-provider session database # Force migration without confirmation php artisan cart:migrate-provider session database --force
Publish Migrations
php artisan cart:publish-migrations
API Reference
CartService Methods
// Core operations add(Model $cartable, int $quantity = 1, array $attributes = []): self update(string|int $itemId, array $data): self remove(string|int $itemId): bool clear(): bool // Information count(): int total(): float subtotal(): float tax(): float items(): Collection get(string|int $itemId): ?Model has(Model $cartable): bool quantity(Model $cartable): int // Cart management exists(): bool delete(): bool clone(string $newCartName, ?string $provider = null): CartService convertToProvider(string $newProvider): CartService // Configuration setConfig(array $config): self getConfig(): array withConfig(CartConfigInterface $config): self // User association forUser(Model $user): self forSession(string $sessionId): self getUser(): ?Model getCartId(): ?int // Meta information getName(): string getProvider(): string
HasCarts Trait Methods
// User cart operations carts(): HasMany getCart(string $name, ?string $provider = null): CartService createCart(string $name, array $config = [], ?string $provider = null): CartService deleteCart(string $name): bool getCartNames(): array hasCart(string $name): bool cloneCart(string $from, string $to, ?string $provider = null): CartService convertCartToProvider(string $cartName, string $provider): CartService
Cartable Trait Methods
// Item cart operations cartItems(): MorphMany isInCart(?string $cartName = null): bool getCartQuantity(?string $cartName = null): int removeFromCart(?string $cartName = null): bool // Required implementations getCartPrice(): float getCartName(): string getCartAttributes(): array
Examples
E-commerce Shopping Cart
// User adds items to cart $user = auth()->user(); $cart = $user->getCart('shopping', 'database'); $product = Product::find(1); $cart->add($product, 2, [ 'size' => 'L', 'color' => 'blue' ]); // Apply discount $cart->setConfig(['discount_rate' => 0.10]); // Checkout process $subtotal = $cart->subtotal(); $tax = $cart->tax(); $total = $cart->total(); // Create order $order = Order::create([ 'user_id' => $user->id, 'subtotal' => $subtotal, 'tax' => $tax, 'total' => $total, ]); // Add order items foreach ($cart->items() as $item) { $order->items()->create([ 'product_id' => $item['cartable_id'], 'quantity' => $item['quantity'], 'price' => $item['price'], 'attributes' => $item['attributes'], ]); } // Clear cart after successful order $cart->clear();
Wishlist Management
$user = auth()->user(); $wishlist = $user->getCart('wishlist', 'database'); $product = Product::find(1); // Add to wishlist $wishlist->add($product); // Check if in wishlist if ($product->isInCart('wishlist')) { // Show "Remove from wishlist" button } // Move from wishlist to cart if ($wishlist->has($product)) { $cart = $user->getCart('shopping'); $cart->add($product); // Remove from wishlist $items = $wishlist->items(); foreach ($items as $item) { if ($item['cartable_id'] == $product->id) { $wishlist->remove($item['id']); break; } } }
Guest to User Cart Migration
// Guest adds items to session cart $guestCart = LaravelMultiCart::cart('guest_shopping', 'session'); $guestCart->add($product, 1); // User logs in $user = auth()->user(); // Get or create user cart $userCart = $user->getCart('shopping', 'database'); // Merge guest cart into user cart foreach ($guestCart->items() as $item) { $cartableModel = $item['cartable_type']; $cartable = $cartableModel::find($item['cartable_id']); if ($cartable) { $userCart->add($cartable, $item['quantity'], $item['attributes']); } } // Clear guest cart $guestCart->delete();
Multi-tenant Cart System
// Configure per-tenant carts $config = new LaravelMultiCartConfig([ 'callbacks' => [ 'unique_item' => function ($cartableId, $cartableType, $attributes) { $tenantId = auth()->user()->tenant_id ?? 'default'; return md5($tenantId . $cartableId . $cartableType . json_encode($attributes)); }, ], ]); $cart = LaravelMultiCart::cart('shopping', 'database')->withConfig($config);
Testing
Run the tests with:
composer test
Run tests with coverage:
composer test-coverage
The package includes comprehensive tests covering:
- All storage providers
- Cart operations
- User integration
- Event dispatching
- Configuration management
- Exception handling
- Performance scenarios
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.