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

Laravel Rating Laravel Package

nagy/laravel-rating

Add ratings to any Eloquent model in Laravel with a simple trait and API. Let users rate items, then retrieve average, sum, and individual ratings. Useful for likes, scores, reviews, or star-style feedback, with flexible rating values and relationships.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package

    composer require nagy/laravel-rating
    

    On Laravel 5.5+ the service provider is auto-discovered. On older versions add Nagy\Rating\RatingServiceProvider::class to config/app.php and the facade (if used) to the aliases array.

  2. Publish and run the migration

    php artisan vendor:publish --provider="Nagy\Rating\RatingServiceProvider"
    php artisan migrate
    

    This creates the package's ratings table (a polymorphic rateable_id / rateable_type pair plus the rating value and a user_id). Publish the config file at the same time and check the table name and user model it points at before migrating — the defaults assume App\Models\User and a users table.

  3. Add the trait to the model you want rated

    use Nagy\Rating\Traits\Ratingable;
    
    class Product extends Model
    {
        use Ratingable;
    }
    
  4. First use case — rate a product and read the score back:

    $product->rate(5, $user);   // or rate(5) to use the authenticated user
    $product->averageRating();  // 4.5
    $product->ratings()->count();
    

Look first at the published config and the migration, then at the trait in vendor/nagy/laravel-rating/src/Traits/. The README on GitHub is short; the trait is the authoritative API surface, and reading it takes five minutes and saves you an hour of guessing method names.

Implementation Patterns

Rate from a controller, not directly in views. Keep the trait calls inside a small RatingService or a form-request-backed controller action so the duplicate-rating guard, auth resolution, and any cache invalidation live in one place.

public function store(RateProductRequest $request, Product $product)
{
    $product->rate($request->integer('rating'), $request->user());

    return back();
}

Guard against double rating. Most rating packages let a user rate once; check for an existing rating before calling rate(), or use the package's own "rate once" variant if it exists in your version. A unique index on (rateable_id, rateable_type, user_id) in the published migration is cheap insurance.

Displaying score and count. Eager-load the relation so listing pages don't fire a query per row:

$products = Product::withCount('ratings')->paginate();
{{ number_format($product->averageRating(), 1) }} / 5
({{ $product->ratings_count }} {{ Str::plural('vote', $product->ratings_count) }})

Sorting by rating. If the package writes an aggregate column onto the model, orderByDesc('rating') works directly. If averageRating() is computed on the fly, either add a cached column updated in an observer, or fall back to a subquery join:

Product::withAvg('ratings as avg_rating', 'rating')->orderByDesc('avg_rating');

Testing. Because the trait writes a polymorphic row, factories are the fastest path: Rating::factory()->for($product, 'rateable')->create(['user_id' => $user->id, 'rating' => 4]);. Assert against the average, not against the row count alone, so a broken aggregate is caught.

API surface. Return { average: float, count: int, my_rating: int|null } from a dedicated endpoint so the front-end star widget doesn't have to know the package's method names. That makes swapping the package later a one-file change.

Gotchas and Tips

The package is stale — plan for it. Last release was April 2021 with zero known dependents. Verify it installs against your Laravel/PHP version before committing; if Composer resolves against an old Illuminate constraint, you'll fight composer update. If you adopt it, pin the version in composer.json and consider vendoring or forking the trait rather than relying on upstream fixes.

Publish the migration before editing it. Never edit the migration inside vendor/ — it will be overwritten. Publish, then add your indexes, cascade rules, and any ->nullable() tweaks to the published copy. A cascadeOnDelete for user_id prevents orphaned ratings when accounts are removed.

Morph maps bite. If you use Relation::enforceMorphMap(), existing rating rows written before the morph map was introduced will have FQCN type values and silently stop resolving. Backfill the rateable_type column or you'll see averageRating() return 0 with no error.

Watch for N+1 on index pages. averageRating() called inside a Blade loop is the classic performance trap in this kind of package. Always pair it with withCount('ratings') and, if you need the average itself in the loop, withAvg().

Auth resolution quirks. rate($value) with no user argument typically pulls auth()->user(). In multi-guard apps (e.g. an admin guard) this may resolve to the wrong user or null, and many packages fail silently in that case. Pass the user explicitly in anything non-trivial.

Float rounding. Ratings are usually stored as integers but averaged to floats. Round at the presentation layer only (number_format($avg, 1)), never in the database, or your sort order and your displayed value will disagree.

Debugging. When a rating "doesn't save", dump $product->ratings()->toSql() and getBindings() to confirm the polymorphic type matches what's in the table. DB::listen() around the request is usually faster than stepping through the trait.

Extension points. The trait is plain PHP — override its methods in your model with parent:: calls if you need custom validation (e.g. reject ratings outside 1–5), and wrap rate() in a model event or dedicated service if the package doesn't emit events. That wrapper is also where you'd hook cache invalidation for any denormalised rating column.

Have an exit plan. Given the package's age and low adoption, keeping the trait calls behind your own Rateable/RatesModels interfaces means you can migrate to a maintained alternative or ~100 lines of hand-rolled code without touching controllers or views.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core