visavi/rotor
Rotor is a modular Laravel admin panel and CMS toolkit from Visavi. It helps you scaffold management interfaces with CRUD resources, authentication, menus, and a responsive UI, so you can manage users, content, and settings without building a backend from scratch.
Rotor is not a Composer library — it is an application skeleton: a Laravel 13 / PHP 8.3+ modular monolith with a modules/ plugin convention and an in-app administration surface. "Integration" therefore means platform adoption or HTTP-level federation, not dependency management. v14.8.0 does not change that framing; it adds evidence about the pace and shape of churn we would own.
Delta in this release that matters to a TPM
php artisan module:migrate applies new migrations for enabled modules and resets settings cache; deploy.php runs it after artisan:migrate. This is the first upstream acknowledgement of the schema-provenance gap identified previously. It converts a hard blocker into a documented pipeline step — but activation-time application and the absence of rollback remain.homepageView hook is removed in favour of Registry::homepage(). Only one minor release after our assessment argued for "hooks over core edits" — hooks are not a stable contract either._language_modal/_comment_edit_modal → *_dialog, data-dialog attribute). Any theme work we commission inherits a rename obligation per release.GET /api/comments, per-list sort values in the spec, structured vote error codes, and module-facing scopes (withUserVote(), capped()/CappableTrait, apiUser()). Tightening: ?user= now requires a token and returns 403 for guests. The surface is still per-module, still unversioned; the token change is a reminder that a federated client has no contract stability guarantee.null + strict_types TypeError is fixed, and error pages now render without a theme (correct status codes, JSON for API). spatie/laravel-ignition is removed, slightly shrinking the dependency-conflict surface.cron.php for path-only cron hosts and hideable admin warnings reveal the target deployment: shared hosting, not container orchestrators. Useful signal for capacity planning, not a feature we would use.Architecture fit
app/; forum, blogs, gallery, language packs, and now feedback and mail notification are modules in modules/. Extension points are module registration, hooks (App\Support\Hook, Registry), Blade components and a declarative data-ajax contract. Fits a product that is a community/content site.php artisan make:module Name --model=Item --admin --api) remains a hard dependency, not a convenience — copying a neighbouring module is documented as losing translations, changelog and model registration.<dialog> and disclosure blocks to <details> (better accessibility, less bespoke JS), and replaces ad-hoc flash handling with notyf. Still no per-module JS bundle and no <script> execution inside ajax partials; anything beyond the {success, message?, html?, redirect?} contract requires core changes.Registry::homepage()) with a FeedService::getLatest() fallback. Cleaner than the removed hook, but it means our theme and any custom homepage are coupled to a registry API that has already changed once.Integration feasibility
composer require path.users table directly is still unwise — core owns that schema and mutates it via database/upgrades/. Prefer a brokered identity mapping.?user= token requirement on user listings; assume further tightening at each minor release and keep the client contract behind our own adapter.Technical risk
AGENTS.md). v14.8.0 is a substantive minor release — evidence of an active maintainer, and of velocity we must track rather than set.App\Classes\* is a bridge "until 15.0"; /mails and /admin/modules/module?module=Name are explicit bridges to 15.0; the homepageView hook is already gone. Assume at least one breaking removal per minor release and one large break at 15.0.database/migrations/ (fresh install) from database/upgrades/ (existing sites); module migrations apply on activation and, as of 14.8.0, can also be applied via module:migrate — but there is still no standard rollback and fresh vs. upgraded paths can diverge. module:migrate is materially better than nothing and should be treated as the load-bearing mechanism, not a safety net.git merge + artisan migrate can leave mail broken and the contact form missing. This is the risk profile of a platform with a plugin marketplace, and it demands a checklist, not diligence.--env=testing without .env.testing silently hits the real database; strict MySQL forbids truncate()/DDL under RefreshDatabase; a known "risky — did not close its own output buffer" failure mode exists. Suite must be made trustworthy before it is a quality gate.rotor-modules; absent in CI/prod it vanishes without error. module:migrate operating on "enabled modules" makes a silent absence harder to notice, not easier.Key questions
?user=)?Stack fit
<dialog>/<details> improve baseline behaviour but provide nothing for a design-system-driven or dense desktop product.upsert restrictions and upgrade files all assume it.cron.php, mail-less operation); we need containers, queue workers and object storage.Migration path (dependency-ordered)
module:migrate, and document exactly what artisan migrate, activation and module:migrate each do and do not do. Exit criterion: can we reconstruct a production schema from git plus one documented activation step, deterministically, in CI? Two weeks, one senior Laravel engineer.artisan:migrate then module:migrate as explicit logged steps, (c) asserts an expected enabled-module count at boot, (d) installs catalog modules we depend on (MailNotify, Feedback, and the content modules) as pinned artifacts rather than in-admin clicks. Then identity, theme, observability, and finally port differentiators as generated modules.?user= change behind our own adapter so upstream API tightening is a config change, not a release.Compatibility
default(0) counters, no partial-column upsert (use update() with CASE), HtmlCast/TextCast for user text, FileService::rules()/attach() (now also the API's file validation path), withUserVote(), capped()/CappableTrait, PlaceCast for normalised user-supplied place names, data-ajax responses with no bespoke window.* handlers. Deviating produces silent sanitisation gaps or TypeError 500s.Registry over core edits — but assume both change at minor cadence and budget a verification pass per upgrade.*_modal → *_dialog) are a one-line change.Sequencing that actually gates
module:migrate behaviour → before deploy design.Maintenance
/mails, /admin/modules/module?module=Name and App\Classes\* all terminate there).module:migrate plus its inclusion in deploy.php gives us a defensible pipeline hook. Still required: a written runbook and a schema-snapshot/diff job across fresh-install and upgraded paths.changelog.md / ## Unreleased convention for our own modules, and maintain a per-upgrade checklist covering: newly extracted modules, removals in "⚠️ При обновлении", theme partial renames, hook changes, and manual data re-saves.Support
AGENTS.md plus our upgrade checklist, and schedule fork-upgrade reviews.Scaling
module:migrate now also resets settings cache.capped() deliberately pages only the first ~1,000 rows per section feed. Product-level cap; unbounded history means changing core and owning the divergence.FileService::attach(); galleries and uploads need dedicated queue workers and object storage sized up front. New attachment ordering/cover selection adds a write path but not new capacity risk.Failure modes, ranked
module:migrate as an explicit logged step.App\Classes\*. Mitigate by banning new App\Classes\* imports, avoiding /mails and legacy admin module URLs, and tracking movement to App\Support\* and registry APIs.homepageView removal break custom themes silently. Mitigate with a theme compatibility layer and a render test on key pages.?user=, per-list sort keys, evolving error shapes. Mitigate with an internal adapter and contract tests.data-ajax responses.truncate() patterns under strict MySQL. Fix early or declare manual QA on those paths.strict_types 500s — retired this release; keep casts on the code-review checklist to prevent regressions.Ramp-up
Recommendation: conditional go, and only as a federated standalone community service — after Legal clears GPL-3.0 and the spike proves reproducible schema including module:migrate and activation on both fresh and upgraded paths. Do not adopt as the base for an existing product. This release improves the operational story (module migrations reachable from artisan, error-page correctness, fewer dependencies) while confirming the two structural costs we must accept: single-maintainer churn to a scheduled 15.0 break, and a platform where upgrading successfully can still remove features unless we install the modules core has shed. This remains an application you take ownership of, not a dependency you consume.
How can I help you explore Laravel packages today?