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

Auto-moderation

Every new review that isn’t force-approved — and every edit to a review’s rating, title or content (see Updating & deleting) — passes through the configured ReviewModerator. It returns a ModerationOutcome: approve, reject (with an optional reason) or pending. The default NullModerator always answers pending, so reviews keep the default status.

Banned-word filter

The bundled, dependency-free WordListModerator auto-rejects any review whose title or content contains a banned word — whole-word, case-insensitive and in any script, so idiót or дурак match like scam does. Clean reviews stay pending for the normal flow:

// config/reviews.php
'moderator' => RoundlyConsulting\Reviews\Moderation\WordListModerator::class,
'moderation' => ['banned_words' => ['spam', 'scam']],

Or keep the list in the environment:

REVIEWS_BANNED_WORDS=spam,scam,casino

A rejected review gets the translatable reviews::messages.review.banned_word message as its meta.rejection_reason.

Your own moderator

Implement the contract and bind it through the moderator config key. It is resolved from the container, so constructor injection works:

use RoundlyConsulting\Reviews\Contracts\ReviewModerator;
use RoundlyConsulting\Reviews\DataTransferObjects\ModerationOutcome;
use RoundlyConsulting\Reviews\Models\Review;

final class TrustedBuyerModerator implements ReviewModerator
{
    public function moderate(Review $review): ModerationOutcome
    {
        if (str_contains((string) $review->content, 'http')) {
            return ModerationOutcome::reject('Links are not allowed');
        }

        if ($review->verified && $review->rating !== null) {
            return ModerationOutcome::approve();
        }

        return ModerationOutcome::pending();   // leave it for a human
    }
}

// config/reviews.php
'moderator' => App\Reviews\TrustedBuyerModerator::class,
  • ModerationOutcome::approve() — approved immediately, approved_at stamped.
  • ModerationOutcome::reject('reason') — rejected, the reason stored under meta.rejection_reason.
  • ModerationOutcome::pending() — left at default_status for a human.

The moderator sees the whole, still unsaved review: $review->author and $review->reviewable are already associated, and $review->exists tells a create (false) from an edit (true, with getOriginal() holding the previous values). Rejected reviews and owner responses are never re-moderated.

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.