NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Reviews for Laravel

Cached aggregates

Live aggregates query on demand. For busy listings, cache reviews_count and reviews_avg on the reviewable’s own table and let the package keep them in sync:

  • Set reviews.cache_aggregates to true (REVIEWS_CACHE_AGGREGATES=true).
  • Publish the reviews-aggregate-migrations stub, rename its placeholder table to your reviewable’s table, and migrate.
  • Add the MaintainsReviewAggregates trait, alongside HasReviews, to that model.
php artisan vendor:publish --tag="reviews-aggregate-migrations"
php artisan migrate
// database/migrations/…_add_review_aggregates_to_reviewable_table.php
Schema::table('products', function (Blueprint $table): void {   // was 'reviewable_table'
    $table->unsignedInteger('reviews_count')->default(0);
    $table->decimal('reviews_avg', 8, 4)->nullable();
});
use RoundlyConsulting\Reviews\Concerns\HasReviews;
use RoundlyConsulting\Reviews\Concerns\MaintainsReviewAggregates;

class Product extends Model
{
    use HasReviews;
    use MaintainsReviewAggregates;
}

Publish the stub once per reviewable table — each publish produces a freshly timestamped migration.

How it stays in sync

An observer recomputes the columns whenever a top-level review is saved, deleted or restored — so creating, editing, approving, rejecting and deleting all update them. It uses the same approved, top-level semantics as the live aggregates, saves quietly (no model events on your subject), and ignores owner responses and subjects without the trait:

// Read and sort on plain columns — no aggregate query per row:
$top = Product::query()->orderByDesc('reviews_avg')->take(10)->get();

$product->reviews_count;   // 320
$product->reviews_avg;     // 4.6000

$product->recountReviews(); // recompute and save quietly on demand

Custom column names

Override the two column methods to match an existing schema:

class Product extends Model
{
    use HasReviews;
    use MaintainsReviewAggregates;

    public function aggregateCountColumn(): string
    {
        return 'rating_count';
    }

    public function aggregateAvgColumn(): string
    {
        return 'rating_average';
    }
}

Rebuilding

Seed existing rows after adding the columns, or rebuild them from scratch at any time. The command walks the model in chunks of 100 and fails if the class isn’t an Eloquent model or doesn’t use the trait:

php artisan reviews:recount "App\Models\Product"

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.