isapp/laravel-cart
Laravel Cart is a customizable shopping cart package for Laravel with persistent database or session storage, guest and authenticated user support, high-precision price calculations, model associations, method chaining, and exception handling.
Architecture fit
isapp/laravel-cart is a cart container, not a commerce engine. It gives you persistence, quantity arithmetic, and BCMath-precise totals over a polymorphic itemable relation; it deliberately excludes tax engines, currency handling, discounts, shipping, stock reservation, and events. For a TPM that distinction decides the build-vs-buy call: the package covers roughly the middle third of a checkout domain, and you own the surrounding two-thirds either way.
The shape is good for Laravel. A Cart facade fronts a Driver contract (database or session), the public API is method-chained and small enough to read end-to-end in one sitting, and Cart::extend('custom', ...) plus contract-typed arguments (CartItemContract, CartItemProduct) give you real seams. Because storeItem accepts the contract rather than a concrete class, you can substitute your own CartItem model later — that is the single most important de-risking property in the package, and it should be preserved in any wrapping layer you build.
Best fit: internal tooling, MVPs, B2B order pads, prototypes with a defined checkout path. Poor fit out of the box: consumer storefronts where cart-to-login merge, promotions, or multi-jurisdiction tax matter on day one.
Integration feasibility
Low technical friction, moderate domain friction. Install is one Composer require, two vendor:publish calls, and php artisan migrate. The one non-obvious integration step is making your Product model implement CartItemProduct with exact return types — prices must be numeric strings, not floats — which is where most first-attempt failures will land.
The real effort is in what the README's own ToDo list admits: no guest→user cart merge, no stock validation, no domain events, no expired-session cleanup, no total item/quantity helpers. Merge-on-login alone is a conversion-critical flow you must design and test yourself.
Technical risk
illuminate/support to ^13. On Laravel 11/12 you must deliberately pin an earlier tag. The absence of a published matrix turns every framework upgrade into a manual investigation.increaseQuantity.Key questions
cart_items, or resolved live from itemable on every read? This determines whether "price at time of add" can be honored.increaseQuantity behave under two concurrent requests? Does the driver use transaction + row lock, or read-modify-write?user_id as bigint, and can it be edited safely before first run for UUID/ULID users tables?Driver treated as stable, or can it grow methods in minor versions (which would break custom drivers)?Stack fit
Requires Laravel with illuminate/support matching the pinned tag, BCMath in the PHP build, and either a relational carts/cart_items schema or a session store. Session-driver deployments on more than one node require a shared session backend (Redis/DB) — otherwise guest carts fragment by node. Add ext-bcmath to the base image and to composer.json platform requirements so it fails at build time rather than at checkout time.
Migration path
Adopt-beside, not adopt-inside. Do not let controllers call Cart:: directly. Introduce one port — App\Cart\CartService — that wraps the facade and exposes only the operations your product needs (add, set quantity, remove, totals, merge). Behind it, the package is one implementation. That wrapper is the entire exit strategy: the package's own API is small enough that a replacement is a sprint, not a quarter, and the wrapper bounds the blast radius of abandonment.
Alongside it, publish and edit before running: adjust user_id column type to match your users table, add an index on session_id, and add a created_at/updated_at-driven TTL column if you want cleanup to be index-friendly.
Compatibility
Pin the exact version in composer.json and lockfile; never float. Record the resolved version and its Laravel constraint in the dependency register. If the team is on L11/L12, identify and pin the last compatible tag explicitly and note that it will not receive fixes. Gate framework upgrades on a documented spike of this package.
Sequencing
Contracts/Driver, Contracts/CartItemProduct, and the published migrations. Exercise guest add, authenticated add, quantity up/down, and totals. Verify BCMath is present.CartService port in place; feature flag or separate route prefix so the cart can be disabled without a revert.storeItem; login listener performing guest→user merge then emptying the session cart; observability hooks at the service boundary (since the package fires no events) for add-to-cart funnels.carts table becomes a growth problem.Maintenance
Low code churn, moderate ownership burden. The package is unlikely to move fast, which is stabilizing, but it also means the ToDo items (merge, events, stock validation) will not arrive from upstream on your schedule — budget them as your code, permanently. Custom drivers are the main maintenance trap: if Driver gains methods in a minor release, every Cart::extend implementation breaks at runtime. Keep custom-driver use to zero unless you need it.
Support
No SLA, no security disclosure process documented, no issue-triage history to benchmark against. Assume self-support. The README is genuinely good — full API listing with examples and error handling — which reduces onboarding cost and makes source reading cheap. Budget one engineer as the named owner and treat upstream PRs as optional.
Scaling
Cart row counts grow monotonically per user/session; without cleanup, cart_items becomes the largest low-value table in the schema within a year. Add the cleanup job in the first release, not later. Per-request cost is one to two queries for the DB driver — negligible against typical page weight, and eager loading is claimed for item reads. Price totals are computed in PHP over the item collection with BCMath; fine at normal cart sizes (tens of items), worth a benchmark if you support bulk/B2B carts in the hundreds. Session driver scales with your session store, not with the package.
Failure modes
| Failure | Impact | Mitigation |
|---|---|---|
| Guest cart lost at login (no merge) | Silent conversion loss; highest severity | Build merge in the login listener before launch |
| Price resolved live, not snapshotted | Totals shift under the customer | Confirm in spike; snapshot price on your own CartItem if needed |
| Concurrent adds race on quantity | Wrong quantities, support tickets | Verify locking; serialize in CartService with a transaction if absent |
| No stock validation | Oversell at checkout | Validate in service layer pre-store and again pre-checkout |
user_id type mismatch in migration |
Deploy-time FK failure | Edit migration pre-run; dry-run on staging snapshot |
| BCMath missing in container | Fatal on first price calc | Base-image requirement + platform check in CI |
| No events fired | Missing analytics; no inventory/promo hooks | Instrument at CartService; single choke point |
| Package abandoned | Frozen dependency | Wrapper port + contract-typed items make replacement a contained sprint |
Ramp-up
A mid-level Laravel engineer reaches productive use in two to four days: the API surface is small, the README covers every method with examples, and storeItem incrementing on duplicate removes a common branch. The cost is not learning the package — it's learning the unwritten behavior (rounding, TTL, concurrency, snapshotting), which is exactly what the spike is for. Do not let a second engineer discover those empirically in production.
How can I help you explore Laravel packages today?