abdukhaligov/nova-rating-field
A Laravel Nova field that adds an interactive star rating input to your resources. Drop it into any Nova form or detail view to let users assign scores with a click, making reviews, priorities, and feedback easy to capture and display.
Install and confirm it loads
composer require abdukhaligov/nova-rating-field
Check whether the package's service provider is auto-discovered (composer.json → extra.laravel.providers). Nova 4+ packages usually are. If not, register it in config/app.php.
Then handle assets. Nova custom fields ship a compiled JS bundle, but it must be published or built:
php artisan vendor:publish --tag=nova-rating-field-assets
# or, if the package expects a build step:
cd vendor/abdukhaligov/nova-rating-field && npm install && npm run prod
Where to look first, in this order
src/ — the Nova field class. This tells you the exact method names available (->min(), ->max(), ->allowHalf(), ->readonly(), or whatever this package actually implements). Do not guess from the README.resources/js/components/ — the Vue component. This tells you what props it accepts and whether it binds to a numeric or string value, which determines your migration column type.composer.json — the laravel/nova version constraint. This is the single most important line for compatibility.First use case
Add a nullable integer column, then drop the field into the resource:
// migration
$table->unsignedTinyInteger('rating')->nullable()->after('id');
// app/Nova/Product.php
use Abdukhaligov\NovaRatingField\Rating;
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Name'),
Rating::make('Rating'),
];
}
Then hit the create form, click a star, save, and confirm the value lands in the database as the integer you expect (1–5, not 0.2–1.0). Verify this before building anything on top of it.
Nullable "unrated" state. Ratings almost always need a three-state model: unrated, rated, and a real value. Keep the column nullable, make the field non-required, and coalesce at display time rather than storing a sentinel 0:
Rating::make('Rating')
->nullable()
->rules('nullable', 'integer', 'between:1,5'),
Half-stars / decimal precision. If the component supports fractional ratings, the column must be decimal(3,1) or similar — a tinyint will silently truncate. Decide up front whether your domain needs half-stars; retrofitting a column type change on a populated table is annoying. If it does not support half-stars, add ->step(1) (or the package equivalent) so the UI cannot produce 3.5 that the DB rejects.
Read-only display on detail and index. Rating fields are frequently shown but rarely edited outside the form. Use ->exceptOnForms() or ->readonly() on detail views, and lean on ->sortable() in the index so reviewers can find the worst-rated records first. Confirm the package supports sorting — many small Nova field packages don't implement sortable() properly and you'll need ->sortable() on a raw column accessor instead.
Aggregate ratings belong elsewhere. This field edits a single row's value. Average-rating-over-reviews is a withAvg() / model-accessor concern. A common and clean pattern:
// Review model keeps the individual rating field.
// Product exposes the aggregate as a separate, non-editable Nova field.
Number::make('Average rating', fn () => $this->reviews_avg_rating ?? 0)
->step(0.1)
->exceptOnForms(),
Mix the two concepts up and you get the classic bug where editing a product's "average" overwrites a derived value.
Shared field configuration. If every resource should present ratings identically, centralize it:
// app/Nova/Fields/RatingField.php
class RatingField extends \Abdukhaligov\NovaRatingField\Rating
{
public function __construct($name = 'Rating', $attribute = null, $resolveCallback = null)
{
parent::__construct($name, $attribute, $resolveCallback);
$this->rules('nullable', 'integer', 'between:1,5')->sortable();
}
}
This is also the cheapest escape hatch when you need to patch behavior without forking the package — subclass, override, ship it in app/.
Non-Nova surfaces. Cast the column explicitly ('rating' => 'integer') and render stars in Blade with a simple loop rather than pulling the Vue component's logic out. Don't try to reuse the Nova field outside Nova; it's a form component, not a presentation helper.
This package is effectively unaudited. Zero dependents, no meaningful star count, single-author. Read src/ and the Vue component end to end before shipping it. It's a small field — a few hundred lines at most — and if the code is thin or unmaintained, writing your own Nova field via php artisan nova:field acme/rating is genuinely a one-evening job with a better support story. Budget for that possibility.
Nova major-version compatibility is the top risk. A field package pinned to laravel/nova: ^4.0 will fail hard on Nova 5 (Vue 3, changed field registration). Check the constraint and check the last commit date. If composer resolution gets ugly, use --with-dependencies and read the conflict output carefully — it's usually the Nova constraint, not PHP.
Assets not rebuilding. The most common "nothing renders" symptom: field meta arrives but no stars appear, and the console shows a missing component. That's a stale or unpublished asset bundle. php artisan nova:publish plus a hard browser refresh fixes it more often than code changes do.
Field meta inspection is your debugger. In the browser network tab, look at the nova-api/{resource} response. Each field carries a meta object of component props. If your min/max/readonly calls aren't there, the method didn't do what you assumed — check the source for the real name.
Null versus zero, again. Sorting a nullable rating column puts unrated records first or last depending on the driver. If "unrated" should sort last, sort by COALESCE(rating, 6) via an indexQuery() override rather than fighting the field.
Validation is server-side only. The star UI constrains input, but curl doesn't. Always attach rules('integer', 'between:1,5') — never rely on the component's click handler as your integrity boundary.
Bulk/mass-assignment. If ratings are edited in bulk (imports, API writes, queue jobs), the Nova field gives you nothing — handle casting, clamping, and null-coalescing in a model mutator or a dedicated action. Reserve the field for interactive editing only.
Extension points, ranked by cost: rules and resolveUsing/displayUsing callbacks → subclassing the field class → overriding the Vue component via ->component('YourComponent') → vendoring the package into app/Nova/Fields. Stop before the last one unless you're prepared to own it permanently.
How can I help you explore Laravel packages today?