centrex / laravel-cart
Shopping cart for Laravel with session, cookie, and database storage, Livewire components, and REST API
Requires
- php: ^8.2
- illuminate/cookie: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^2.0
- laravel/pint: ^1.0
- nunomaduro/collision: ^8.5
- orchestra/testbench: ^9.5
- pestphp/pest: ^3.4
- pestphp/pest-plugin-arch: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- rector/rector: ^1.2
- spatie/laravel-ray: ^1.26
Suggests
- livewire/livewire: Required for CartIcon and CartDrawer Livewire components (^3.0)
This package is auto-updated.
Last update: 2026-04-21 03:09:24 UTC
README
A full-featured shopping cart for Laravel. Supports session, cookie, and database storage drivers, multiple named cart instances (e.g. wishlist), Livewire components (CartIcon, CartDrawer), and a REST API — all swappable via a single config key.
Installation
composer require centrex/laravel-cart
php artisan vendor:publish --tag="laravel-cart-config"
For the database driver, run:
php artisan vendor:publish --tag="laravel-cart-migrations"
php artisan migrate
Configuration
php artisan vendor:publish --tag="laravel-cart-config"
// config/laravel-cart.php 'driver' => env('CART_DRIVER', 'session'), // session | cookie | database 'default_instance'=> 'default', 'tax' => env('CART_TAX', 0), // integer percent, e.g. 15 'cookie_lifetime' => env('CART_COOKIE_LIFETIME', 43200), // minutes (default 30 days) 'database' => [ 'connection' => env('CART_DB_CONNECTION'), 'table' => 'carts', ],
Drivers
| Driver | Persists across requests | Auth-aware | Best for |
|---|---|---|---|
session |
Until session expires | No | Default, server-rendered apps |
cookie |
30 days (configurable) | No | Guest carts, headless/SPA |
database |
Permanently (until cleared) | Yes — uses user ID for auth, session ID for guests | Authenticated users, cart recovery |
Usage
Facade
use Centrex\Cart\Facades\Cart; // Add item (merges qty if same rowId already exists) $item = Cart::add( id: $product->id, name: $product->name, qty: 2, price: $product->price, options: ['color' => 'red', 'size' => 'M'], ); echo $item->rowId; // md5 hash of id + sorted options echo $item->subtotal; // qty × price // Update quantity Cart::update($item->rowId, 5); // Remove item Cart::remove($item->rowId); // Clear everything Cart::clear();
Reading the cart
// All items — Collection<string, CartItem> Cart::content(); // Single item (throws CartItemNotFoundException if missing) Cart::get($rowId); // Aggregates Cart::count(); // total units (sum of all qty) Cart::lines(); // number of distinct line items Cart::subtotal(); // sum of all item subtotals Cart::tax(); // subtotal × (tax% / 100) Cart::total(); // subtotal + tax Cart::isEmpty();
CartItem properties
$item->rowId; // string — md5(id + sorted options) $item->id; // string|int — your product ID $item->name; // string $item->qty; // int $item->price; // float — unit price $item->options; // array — ['color' => 'red', 'size' => 'M'] $item->subtotal; // float — qty × price $item->toArray();
Multiple cart instances
Use instance() to work with independent named carts. Returns a new Cart object — does not mutate the original singleton.
// Default cart Cart::add(1, 'Widget', 1, 29.99); // Wishlist cart $wishlist = Cart::instance('wishlist'); $wishlist->add(2, 'Gadget', 1, 99.00); Cart::count(); // 1 (default) $wishlist->count(); // 1 (wishlist)
Events
Listen to cart events in your application's EventServiceProvider:
use Centrex\Cart\Events\CartItemAdded; use Centrex\Cart\Events\CartItemUpdated; use Centrex\Cart\Events\CartItemRemoved; use Centrex\Cart\Events\CartCleared; Event::listen(CartItemAdded::class, function (CartItemAdded $event) { // $event->item — CartItem // $event->instance — 'default' });
Cookie storage — guest carts
CART_DRIVER=cookie CART_COOKIE_LIFETIME=43200 # 30 days in minutes
Note: Cookie writes are queued for the HTTP response. A
get()call in the same request reads what the browser already sent, not what was queued. This is standard browser behaviour.
Database storage — persistent & auth-aware
CART_DRIVER=database
- Guests are identified by
session()->getId(). - Authenticated users are identified by
auth()->id(). - Cart rows are keyed on
(instance, identifier)— unique index.
To migrate a guest cart to a user after login:
// In your LoginController / AuthenticatedSessionController use Centrex\Cart\Models\StoredCart; StoredCart::where('identifier', session()->getId()) ->update(['identifier' => (string) auth()->id()]);
Livewire Components
Requires livewire/livewire ^3.
CartIcon
Displays a shopping bag icon with an item count badge. Updates automatically when the cart changes.
{{-- In your navbar / header --}} <livewire:cart-icon />
Clicking the icon dispatches the open-cart browser event, which opens CartDrawer.
CartDrawer
A slide-out panel showing the full cart with quantity controls, item removal, and totals.
{{-- Once, anywhere in your layout (e.g. before </body>) --}} <livewire:cart-drawer />
Open the drawer from any element:
{{-- Alpine.js dispatch --}} <button @click="$dispatch('open-cart')">View Cart</button> {{-- Or from a Livewire component --}} $this->dispatch('open-cart');
Triggering a cart update from your own Livewire components
After adding an item to the cart in a Livewire component, dispatch cart-updated so CartIcon and CartDrawer refresh automatically:
// In your ProductCard or similar Livewire component public function addToCart(int $productId): void { $product = Product::findOrFail($productId); Cart::add($product->id, $product->name, 1, $product->price); $this->dispatch('cart-updated'); // refreshes CartIcon badge + CartDrawer $this->dispatch('open-cart'); // optionally open the drawer }
Publish views for customisation
php artisan vendor:publish --tag="laravel-cart-views" # → resources/views/vendor/laravel-cart/livewire/cart-icon.blade.php # → resources/views/vendor/laravel-cart/livewire/cart-drawer.blade.php
REST API
All endpoints are prefixed with api (configurable via api_prefix) and use the api middleware (configurable via api_middleware).
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/cart |
Get full cart (items, count, subtotal, tax, total) |
POST |
/api/cart/items |
Add item to cart |
PUT |
/api/cart/items/{rowId} |
Update item quantity |
DELETE |
/api/cart/items/{rowId} |
Remove item |
DELETE |
/api/cart |
Clear cart |
GET /api/cart
{
"data": {
"instance": "default",
"items": [
{
"row_id": "b8b0b1b2...",
"id": 1,
"name": "Widget",
"qty": 2,
"price": 29.99,
"subtotal": 59.98,
"options": { "color": "red" }
}
],
"count": 2,
"lines": 1,
"subtotal": 59.98,
"tax": 0.0,
"total": 59.98
}
}
POST /api/cart/items
{
"id": 1,
"name": "Widget",
"qty": 2,
"price": 29.99,
"options": { "color": "red", "size": "M" }
}
PUT /api/cart/items/{rowId}
{ "qty": 5 }
DELETE /api/cart/items/{rowId}
Returns 204 No Content.
DELETE /api/cart
Returns 204 No Content.
Testing
composer test # full suite composer test:unit # pest only composer test:types # phpstan composer lint # pint
Changelog
Please see CHANGELOG for more information on what has changed recently.
Credits
License
The MIT License (MIT). Please see License File for more information.