---
title: "Cached aggregates — Reviews for Laravel | Roundly"
description: "Store reviews_count and reviews_avg on the subject’s table, keep them in sync automatically, rebuild with reviews:recount."
url: https://roundly-consulting.com/open-source/docs/reviews-for-laravel/cached-aggregates
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Reviews for Laravel](https://roundly-consulting.com/open-source/docs/reviews-for-laravel.md)

# 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.

```bash
php artisan vendor:publish --tag="reviews-aggregate-migrations"
php artisan migrate
```

```php
// 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();
});
```

```php
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, writes only the two columns with a targeted update (no model events, no updated\_at bump, no other unsaved change on your subject), and ignores owner responses and subjects without the trait:

```php
// 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 write the two columns on demand
```

Concurrent reviews never cost a count. The recount runs in one transaction under the subject’s row lock and counts with locking reads, and inside a transaction the observer locks the subject before the review row is written. Every writer of one subject takes its locks in the same order, so two approved reviews arriving at once serialize instead of deadlocking or losing a count.

## Custom column names

Override the two column methods to match an existing schema:

```php
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:

```bash
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](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## 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.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/reviews-for-laravel.md)
