rmscms/shop
rmscms/shop is a Laravel package for adding e-commerce to RMS CMS: product catalogs, categories, carts, orders, and checkout. It provides straightforward store management and a lightweight storefront foundation for launching online shops without heavy dependencies.
Architecture fit. rmscms/shop is a conventional Laravel package — service provider, publishable config/migrations/Blade views, Eloquent models for products, categories, carts, and orders. That is a low-friction shape for any Laravel application. The real fit question is not Laravel, it is RMS CMS: the package is explicitly a shop layer for that CMS, with "RMS CMS integration for consistent admin and frontend workflows" called out as a feature. If we are not running RMS CMS, we are adopting the data model and admin affordances of a CMS we do not use, and paying for that coupling on every upgrade. That is a scoping decision to make before any code is written, not after.
Integration feasibility. Mechanically straightforward: composer require, confirm auto-discovery via php artisan about, publish config/migrations/views by tag, add env vars, and prove wiring with one feature test that resolves the package's main contract from the container and asserts a happy path. Half a day for a Laravel-fluent engineer. The feasibility risk is in coverage, not mechanics.
Technical risk. Three items dominate:
Key questions to put to the maintainer (or resolve by reading the source) before committing:
rmscms/core (or equivalent) a hard composer dependency, or is the CMS integration optional/loose?extend()), or event dispatch on order lifecycle?orders/users schema?Stack fit. Determine first whether our storefront is Blade or a JS frontend. If we render with Inertia/Livewire/Vue, the published Blade views are near-worthless to us and the value collapses to the Eloquent models and migrations — which materially changes the build-vs-buy math. Confirm PHP/Laravel constraints against our runtime before the spike, not during it.
Migration path. Timebox a 5-day spike on a branch with explicit exit criteria:
config/shop.php end to end — it is the most accurate documentation of the knobs and their defaults.php artisan migrate:status, and check ordering and table-name collisions against our schema. Rename timestamps if ordering matters.Compatibility. Verify Octane safety: a shop package that caches cart or config state on a singleton will leak between requests. Check whether bindings are scoped or whether a ResetInterface listener exists. Also confirm auto-discovery is not disabled in our composer.json and that no facade aliases are required in our skeleton.
Sequencing. Do not build checkout first. Order of work:
CatalogService, CartService, CheckoutService, OrderService — bound in a service provider. This is the single highest-leverage step: it makes an abandoned package a one-file replacement instead of a rewrite, and it keeps us from reading package config keys at fifty call sites.extend() exists; otherwise via event listeners. Idempotency and order-vs-payment reconciliation are ours to build regardless.Exit criteria for the gate. Proceed only if: the package runs on our Laravel version, money is stored correctly, table collisions are resolvable, checkout can be extended without editing vendor code, and we are either on RMS CMS or have accepted the coupling cost. If any of these fail, the honest recommendation is to take the migrations and model shapes as a starting point and build the thin layer ourselves — the described surface is not large.
Maintenance. Treat this as vendored code with an upstream. Pin to a caret constraint, commit composer.lock, and read the changelog before any minor bump. Budget a standing allocation — realistically 10–20% of one Laravel engineer — for published-view drift, migration ordering, and config reconciliation. Pre-agree a fork policy now: if upstream goes quiet, we fork to a VCS repository in composer.json and patch there. Deciding this after an incident is expensive.
Support. No commercial support, no visible maintainer bench. Our escalation path is our own team, and our on-call runbook must reflect that. Capture an ADR at the gate recording why we chose this package, what we wrapped, and what would trigger a replacement — future engineers will need it and the GitHub signal will not explain the decision.
Scaling. Catalog reads are cacheable and cheap. The pressure points are cart writes (every add/update hits the DB unless we move cart state to Redis/session) and checkout latency, which sits on the user's critical path. Push all non-blocking work — confirmation emails, ERP sync, webhook processing — to queued jobs with explicit $timeout below the worker timeout, retries, and backoff. Index orders on customer and status from day one; order-listing queries are the first thing to degrade.
Failure modes to plan for explicitly.
config:clear && config:cache after every env change, as a deploy-script step, not a manual habit.vendor:publish --force clobbering edited config or views. Diff before forcing; commit published config so drift is reviewable.Ramp-up. The package surface is small and idiomatically Laravel, so a mid-level engineer is productive in roughly a week. The real ramp is domain, not code: tax, refunds, PCI scope, and reconciliation. Do not staff this as "a Laravel ticket." Pair the implementation with someone who owns the commercial policy, and treat payment handling as a compliance-adjacent workstream from the start.
Bottom line. Use rmscms/shop as a catalog and admin skeleton behind our own interfaces if — and only if — the gate above passes and ecommerce is adjacent to the product rather than the product itself. Do not let it own checkout, money, or inventory as the system of record without a written, tested extension layer and a named owner.
How can I help you explore Laravel packages today?