joelwmale/laravel-cart
A simple shopping cart package for Laravel with support for multiple cart instances, quantities, prices, taxes, discounts, and persistent storage. Offers an easy API for adding, updating, and removing items, calculating totals, and managing carts across sessions.
This is a major release, so start by confirming your runtime before composer update:
php -v # must be >= 8.3
composer show laravel/framework | grep versions # must be ^11.0|^12.0|^13.0
Install/upgrade the package, then publish any config if the package ships one (php artisan vendor:publish --tag=cart-config) and clear cached config/routes so the new session-load hooks register cleanly:
composer require <vendor>/<cart> "^2.0"
php artisan config:clear && php artisan cache:clear
Where to look first: the cart service/provider binding and the session key used to persist cart contents. That session payload is now sanitised on every load, so it's the single most important thing to understand before you debug anything.
First use case: add an item, reload the page, and confirm it survives the round trip:
use App\Facades\Cart;
Cart::add([
'id' => 1,
'name' => 'Widget',
'quantity' => 2,
'price' => 19.99,
]);
// Next request
$items = Cart::content(); // invalid rows are already gone
If that works, you've validated the two things that break in v2: the new PHP/Laravel floor and the automatic removeInvalidItems() pass on session load.
Treat removeInvalidItems() as a safety net, not a validation layer. It runs silently on every cart session load and strips rows missing id, name, or quantity. Keep validating at the boundary (form requests, add-to-cart actions) so users get errors instead of items quietly disappearing between requests.
// Explicit, loud validation at the edge
$validated = $request->validate([
'id' => ['required', 'integer'],
'name' => ['required', 'string', 'max:255'],
'quantity' => ['required', 'integer', 'min:1'],
]);
Cart::add($validated);
Wrap cart mutation in a service or action class. Session-backed carts get awkward when logic lives in controllers, and the v2 auto-cleanup makes it easy to hide bugs in "add" payloads. A thin CartService gives you one place to enforce the required keys and one place to test them.
Normalise on read for display. Because malformed rows are now dropped rather than throwing, your views should still be defensive about optional fields (price, options, image) that removeInvalidItems() does not check:
$total = Cart::content()->sum(
fn ($item) => ($item['price'] ?? 0) * $item['quantity']
);
Use it in queue jobs and console commands carefully. The cart is session-bound; anything running outside an HTTP request with a session will see an empty or unexpected cart. For order creation, snapshot Cart::content() into a DTO or DB row inside the request and pass that to the job.
Testing pattern: assert against the session store directly, and add a regression test for the new behaviour:
$this->withSession(['cart' => [
['id' => 1, 'name' => 'Valid', 'quantity' => 1],
['name' => 'Missing id', 'quantity' => 1], // should be stripped on load
]])->get('/cart');
$this->assertCount(1, Cart::content());
Integrate with Laravel 11+/12/13 idioms. Prefer constructor or method injection of the cart contract over the facade in new code, use #[Validate]-style or FormRequest validation consistently, and if you use Laravel 11's slim skeleton, remember there's no app/Http/Kernel.php — session middleware is configured in bootstrap/app.php, which is where any cart session behaviour is ultimately governed.
removeInvalidItems() runs on every session load. If a prior version of your code wrote cart rows under different key names (e.g. product_id, title, qty), those rows are now deleted without notice on the next request. Audit any custom cart writers before deploying. If you have a migration path, backfill session data or key names rather than relying on the cleanup.id, name, quantity. Missing price, options, or nested arrays pass through untouched and can still cause notices, null totals, or division errors downstream. Guard those yourself.quantity of 0 or a negative value may not be considered "missing." The stated rule is presence of the field, not validity. Validate ranges yourself; don't expect the cleanup to remove quantity => -1.session('cart') in a middleware or a dd() in a service provider boot() — because by the time you call Cart::content() the evidence has already been removed.composer update, php artisan config:clear, re-run your full test suite, and specifically re-test anything that reads the cart on a fresh session (guest checkout, empty-cart states). The cleanup pass makes "empty cart" states more likely to appear in tests that previously asserted on malformed fixtures.How can I help you explore Laravel packages today?