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

The Reviews facade

Everything the package does is one call away on the Reviews facade — RoundlyConsulting\Reviews\Facades\Reviews, also registered as a global Reviews alias:

use RoundlyConsulting\Reviews\DataTransferObjects\CreateReviewData;
use RoundlyConsulting\Reviews\DataTransferObjects\UpdateReviewData;
use RoundlyConsulting\Reviews\Facades\Reviews;

// One subject: author a review, read its aggregates, list its reviews
Reviews::for($product)->by($user)->rating(5)->content('Great')->create();
Reviews::for($product)->summary();            // RatingSummary DTO
Reviews::for($product)->average();            // ?float
Reviews::for($product)->count();              // int (approved)
Reviews::for($product)->distribution();       // [5 => 120, 4 => 80, ...]
Reviews::for($product)->photoCount();         // photos on approved reviews
Reviews::for($product)->reviewsWithPhotos();  // approved reviews that carry a photo
Reviews::for($product)->query()->approved()->mostHelpful()->paginate();

// Every review (swap-aware) — e.g. the moderation queue
Reviews::query()->pending()->latestFirst()->paginate();

// Act on one review
Reviews::create(new CreateReviewData(author: $user, reviewable: $product, rating: 5));
Reviews::approve($review);
Reviews::reject($review, 'Spam');
Reviews::update($review, new UpdateReviewData(content: 'Edited'));
Reviews::delete($review);
Reviews::respond($review, $owner, 'Thanks!');
Reviews::vote($review, $user, helpful: true);
Reviews::removeVote($review, $user);
Reviews::verify($review);
Reviews::unverify($review);

// Tests
$fake = Reviews::fake();

Methods

MethodReturnsDoes
for(Model $reviewable)ReviewableScopeScopes the calls below to one subject.
for(…)->by(Model $author)PendingReviewStarts a review of the subject; finish with ->create().
for(…)->summary() / average() / count() / distribution() / photoCount() / reviewsWithPhotos()aggregatesApproved, top-level reviews of the subject only.
for(…)->query()Builder<Review>The subject’s top-level reviews, every status (responses via $review->responses).
query()Builder<Review>Every review on the configured model — every subject, status and response.
create(CreateReviewData $data)ReviewCreates a review from a DTO — the builder’s terminal step.
approve() / reject() / update() / delete()Review / voidThe moderation lifecycle.
respond(Review, Model $author, string $content, ?string $title)ReviewCreates an owner response.
vote(Review, Model $voter, bool $helpful) / removeVote(Review, Model $voter)ReviewVote / boolHelpful votes (removeVote() is false when there was no vote).
verify(Review) / unverify(Review)ReviewSets or clears the verified flag; fires ReviewVerified.
fake()ReviewsFakeSwaps in the recording fake — see Testing.

One subject: Reviews::for()

for($subject) returns a ReviewableScope with the subject fixed at construction — nothing it returns reaches another subject’s rows. Its aggregates count approved, top-level reviews only; its query() returns the subject’s top-level reviews in every status. Reviews::query() is the wider, swap-aware query over every review on the configured model.

Model helpers use the same manager

$product->addReview(), the HasReviews aggregates, $review->respond(), $review->markVerified() / markUnverified() and the CanVoteOnReviews helpers all delegate to the same manager. A container override therefore applies everywhere, and Reviews::fake() sees every call.

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.