Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tomato Ecommerce Laravel Package

tomatophp/tomato-ecommerce

Tomato Ecommerce is a Laravel package for TomatoPHP that adds product catalogs, carts, checkout, and order management to your app, with Filament admin integration for handling products, orders, and store settings.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with an exact pin. composer require tomatophp/tomato-ecommerce:1.0.8 — tag, not caret. The 1.0.x line ships frequent, thinly-documented patches (v1.0.8 is a single "update icon" commit), so a floating constraint means surprise upgrades with no changelog to diff. Land the lockfile change on its own branch so a Filament/Laravel constraint conflict fails in isolation rather than in the middle of a feature branch.

Read the dependency contract before the code. composer.json first — php, laravel/framework, and filament/filament ranges tell you in thirty seconds whether this can coexist with the other tomatophp/* modules you already run. TomatoPHP modules move in lockstep against a shared Filament constraint; this is the check that saves you a broken admin panel later.

Then the schema, then the Filament resources. database/migrations/ is the real API surface of an admin-first package — read the table names, column types, nullability, and indexes before you read a single model. After that, the Filament resource classes (typically under src/Filament/ or registered through a panel plugin) tell you what the package thinks it manages versus what it expects you to build. The gap between those two is your integration surface.

Publish, migrate, seed — in a throwaway app. Publish config and migrations, run php artisan migrate, and run any seeders. The demo catalog is the fastest route to a working golden path, and the migration run tells you immediately whether the schema collides with tables you already have. Do not do this on your main app first.

First use case: walk the golden path by hand before writing code. In the Filament panel, create a product with a SKU and a price, add it to a cart (storefront route if one ships, tinker if not), run checkout, and confirm an order row appears with the totals and status you expect. One hour, and it answers the most important unknown about this package: which capabilities are actually implemented and which are stubbed. The description says "cart and checkout flow" and "order management" — it does not say payment, tax, shipping, or refunds, and you should assume those are yours until the golden path proves otherwise.

Write down the boundary while it's fresh. Typical split: the package owns catalog, cart persistence, order records, and admin CRUD; you own payment gateway calls, tax, shipping rates, webhook handling, refunds, and any non-Filament storefront UI. Confirm that on day one — the alternative is discovering it three weeks in, mid-integration.

Implementation Patterns

Treat it as a back-office toolkit, not a commerce engine. The models are shaped around Filament resources, not around a headless API contract. The most maintainable pattern is to leave the package's models as pure persistence and put orchestration in a thin application-level service — App\Services\CheckoutService, App\Services\CartService — that drives cart → order → payment. When a patch commit renames a method, your service layer absorbs the blast radius instead of every controller and Livewire component.

Do not edit published migrations; add your own. If you need extra order fields, prefer a HasOne profile table or a side table keyed on order_id. If you must add columns to a package table, publish the migration and then write a new migration for your columns — never touch the published file, or the next upgrade silently reverts you and you find out at deploy time.

Cart lifecycle is yours to own explicitly. The common shape here is a session-backed cart for guests, merged into a user-bound cart on login. Implement the merge yourself as an explicit call on the Login event; do not assume the package handles cross-device merging correctly. Recompute totals server-side on every state change and never trust a total posted from the client. Check whether the package stores a totals snapshot on the order row — you want it to. A snapshot is what makes a historical invoice defensible when product prices change next quarter.

Put order state transitions behind a guard. The package will ship some status field; it almost certainly will not ship a full state machine. Add a small PHP enum plus a single transition(OrderStatus $to) method that rejects illegal moves — pending → paid → fulfilled, with refund as a branch, not a rollback of the previous status. Log every transition with actor, timestamp, and reason. Twenty lines now; prevents "refunded after shipment" being an unrecoverable dead-end later.

Payments go behind an interface and outside the request path. One PaymentGateway contract with charge, refund, verifyWebhook. Create the order in pending, redirect to the provider, and let a queued webhook job confirm it. Idempotency key on order creation, unique index on the provider transaction ID. If the package dispatches a payment-related event, hook it; if not, fire your own from the service layer — do not patch vendor code to add one.

Filament is the intended customization path and it is cheap. Custom columns, status badges, action buttons, and filters on the package's resources are registered by extending the resource class or through a panel plugin. "Mark as shipped," "resend confirmation email," "export order lines" are twenty-line actions that survive upgrades far better than a fork. If your team's customization instinct is to copy the resource into app/Filament/, resist it — subclass or plugin instead.

Feature-flag the whole integration. Wire the package's routes and checkout behind config('features.new_checkout'). In a replacement scenario, run it in shadow mode: real orders keep going through the old flow while you write parallel order records through the new one, and diff totals daily for a week. That is the only reliable way to catch total-calculation drift before it costs money.

Test the golden path, one feature test per step. RefreshDatabase against the package migrations: product create → cart add → checkout → order confirmed. If the package ships factories, use them; if not, write factories for its models in your first week, because you will need them constantly for every downstream test. Assert on the totals snapshot, not just the order's existence.

Gotchas and Tips

Ecosystem lockstep is the top operational risk. A patch in an unrelated TomatoPHP module can force a bump here, and the failure mode is a white-screen admin page or a silently missing resource — not a clean Composer error. Pin every tomatophp/* package, and rehearse family-wide upgrades on a branch where you actually load and click through the panel.

Release notes will not help you. v1.0.8 is one commit, "update icon." Before every upgrade, read git diff v1.0.7..v1.0.8 -- src database yourself, and run git show <tag> -- composer.json as a two-second check for hidden constraint bumps. Assign this to a named engineer; it is recurring work, not zero.

Decide schema ownership and write it down. Either (a) let the package's migrations run untouched in your migration path, or (b) publish and manage them yourself. Option (a) gives you upgrade ordering you do not control and rollbacks that can drop data you assumed was yours. Option (b) means manual reconciliation of upstream migration changes. Both are defensible; drifting between them is not.

Brownfield collisions happen here. If you already have products, carts, or orders, diff the generated schema against yours before writing code. Mapping into existing tables is usually more work than running the package's tables alongside and syncing — and syncing is almost always the wrong answer for revenue data.

Assume no inventory locking until you prove otherwise. Concurrent checkouts on the last unit will both succeed. Wrap stock decrement in a transaction with lockForUpdate(), or add a reservation table with an expiry, and test it with a real concurrency harness rather than a unit test. This is the bug that produces angry customer emails rather than a stack trace.

Idempotent webhooks or you will double-charge. Providers retry. Without a unique constraint on the provider transaction ID and an idempotency key on order creation, retries create duplicate orders. Guard it before launch, not after the first incident.

Filament table performance degrades quietly. Admin list pages over a large order or product table will N+1 and full-scan. Check the generated indexes on SKU, order number, and status; add your own if missing. Turn on the query log and paginate 50k orders once — that is where a "working" install starts timing out.

Queue dependency produces silent failures. If confirmation emails and payment verification run on the queue, a dead worker gives you orders that exist but never confirm. Alert on queue depth and failed-job count; do not rely on a dashboard someone checks weekly.

Config quirks to verify explicitly, five minutes each: default currency and where formatting happens (raw values in the DB, formatting at the edge — never store formatted money); whether prices are stored tax-inclusive or exclusive; the default order status; whether routes register automatically or need a panel provider entry; and whether the module loader requires explicit registration in config/tomato.php or similar.

Debug checkout in this order: does the order row exist and with what status → does the totals snapshot match a server-side recompute → was a gateway charge attempted (provider log) → was confirmation dispatched (queue). Most "checkout is broken" reports resolve at step two, as a totals drift bug.

Locate extension points early: events dispatched on cart/order changes (hook notifications and analytics there), the Filament resource registration (admin actions), and the config file (currency, statuses, route prefixes). If no events exist, dispatch your own from the service layer — do not fork to add them.

Confirm the rollback path in a disposable app before this touches anything that matters. If php artisan migrate:rollback leaves orphaned tables or drops data, you need a documented restore procedure — written down, not improvised — ahead of production rather than during an incident.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource