nikaia/nova-rating-field
A Laravel Nova field that adds interactive star ratings to your resources, letting users rate records with a configurable star scale directly in the Nova UI. Supports custom icons, half stars, and read-only display for indexes and detail views.
Architecture fit. This is an admin-plane component, not a product capability. It plugs into Nova's field contract — a PHP Field subclass plus a Vue component registered through Nova::script() / Nova::style() — and touches nothing in the domain model, API surface, or customer-facing rating logic. That containment is its main architectural virtue: blast radius is limited to Nova resource screens. The fit is conditional, though. It is a genuine drop-in only on Nova 1.x/2.x with PHP 7.x. On Nova 3/4/5 the Vue side is pre-Vue-3 (value prop + $emit('input')) and will not bind to Nova 4's update:value model, which makes it reference source rather than a dependency.
Integration feasibility. Mechanically easy: composer require, service provider auto-discovery, no publish step, php artisan nova:publish plus view:clear. But Composer exit code is not evidence of compatibility — the real feasibility gate is a browser spike: load the form, click a star, save, reload, and check index, detail, and create views with seeded null / 1 / 5 rows. Budget half a day for that spike before committing to anything. Two preconditions must be confirmed first: an active licensed Nova panel at a supported major, and whether dist/ is actually committed (if not, you inherit a 2019 webpack/Babel toolchain and the build debt changes the estimate materially).
Technical risk. High relative to the value delivered. The package has zero dependents, no release since 2019-12-24, no upstream maintainer, and no security-review trail. The Vue API drift between Nova 2 and Nova 4 is a component rewrite, not a patch — if the stars render but never persist, that is the cause. Secondary risks are operational rather than architectural: stale compiled JS after deploys, per-row component instances blowing out large index pages, missing integer casts producing a silently blank widget, and the common inability to clear a rating by clicking the same star.
Key questions.
dist/ committed, or would we be rebuilding a 2019 front-end toolchain?Number/Select with integer|between:1,5 an acceptable UX?Bottom line. Do not adopt as a Composer dependency. Use the repository as a design reference (roughly 150–200 LOC across two files) and replicate the behavior in-house, or fall back to a plain numeric field if the star affordance doesn't clear the UX bar.
Stack fit. Laravel + Nova + PHP 7.x + Vue 2. That is the only configuration where this is a drop-in. If the estate is Nova 4/5, there is no stack fit — the correct move is to scaffold an in-house field through nova.mix.js, register with Nova.booting((app) => app.component(...)), and use the update:value model. Filament or Orchid shops should not port this at all; maintained star components exist there.
Migration path. Minimal and reversible. One migration adds a nullable unsignedTinyInteger column; the model casts it to integer (mandatory — without the cast Eloquent hands a string to the JSON payload and the star fill comparison fails silently). Existing rows backfill as null, which should be documented as the canonical "not scored yet" state. Reserve 0 for an explicit product decision, enforced at the model or database layer, not by the widget.
Compatibility. Pin and record the full matrix — Nova ↔ Laravel ↔ PHP ↔ Vue — in a vendored-UI register. Treat any Nova upgrade as a breaking-change event for this field until re-tested. Validation must live server-side regardless of what the widget renders: keep nullable|integer|between:1,{max} on the field and in the FormRequest, because Nova field rules() only fire on the Nova form path, not imports, seeders, or API writes.
Sequencing.
null / 1 / 5 rows across index, detail, and create/update.app/Nova/Fields/StarRating.php, drop the Composer dependency, and centralize configuration (max, validation range, column type) in a HasRatingField trait so the three can never drift.Number field so rollback is one deploy.belongsTo create, 100-row index pages, mobile Safari) are verified.Scope discipline. Pair the input field with our own aggregate/display layer if anything shows "4.2/5" to users. The widget is an operator-input affordance and must not be allowed to shape the customer-facing rating model.
Maintenance. Adoption equals ownership. With zero dependents and no upstream releases, there is no community safety net. The recurring cost is not code churn — the field is small and stable — it is mandatory re-validation at every Nova, Laravel, PHP, and Vue major upgrade. That cost is real and should be budgeted as a standing line item, not an afterthought.
Support. None. No vendor SLA, no security advisories, no issue triage. MIT makes vendoring trivial and legally clean; it does not make the code supported. Any bug found is ours to fix, and any vulnerability is ours to discover.
Scaling. Two constraints. First, rendering: per-row Vue component instances in a 100-row index table can degrade page performance — prefer a plain text or Number display in the index and reserve the star UI for forms and detail. Second, capability: the package provides display and input only. "Show me all unrated items," sorting by rating, and aggregate scores all require hand-written Nova filters, whereNull scopes, and our own computed columns. None of that comes for free, and it is easy to under-scope in a pitch.
Failure modes to pre-plan for.
field.js — relative path passed to Nova::script(), which requires an absolute path.integer cast.null vs 0 vs empty string) at the model boundary.nova:publish and hard-refresh when validating.aria-label, focus styles, and space/enter handling at build time; retrofitting after operators have built muscle memory is materially worse.Ramp-up. Documentation is thin, so onboarding means reading two files in vendor/nikaia/nova-rating-field/src/ and dist/. A competent Laravel developer needs roughly half a day to spike and half a day to vendor and harden an in-house equivalent. That is a low, well-bounded cost, and it is the strongest argument for building rather than depending: the in-house path removes the unmaintained dependency entirely while producing the same operator experience.
How can I help you explore Laravel packages today?