Configuration
The package works with zero configuration. The published config/reviews.php in full:
<?php
use RoundlyConsulting\Reviews\Enums\ReviewStatus;
use RoundlyConsulting\Reviews\Models\Review;
use RoundlyConsulting\Reviews\Models\ReviewVote;
use RoundlyConsulting\Reviews\Moderation\NullModerator;
return [
'model' => Review::class,
'vote_model' => ReviewVote::class,
'key_type' => env('REVIEWS_KEY_TYPE', 'bigint'),
'min_rating' => env('REVIEWS_MIN_RATING', 1),
'max_rating' => env('REVIEWS_MAX_RATING', 5),
'default_status' => ReviewStatus::Pending->value,
'auto_approve' => env('REVIEWS_AUTO_APPROVE', false),
'reset_status_on_edit' => env('REVIEWS_RESET_STATUS_ON_EDIT', true),
'one_per_author' => env('REVIEWS_ONE_PER_AUTHOR', false),
'register_facade_alias' => env('REVIEWS_REGISTER_FACADE_ALIAS', true),
'moderator' => NullModerator::class,
'moderation' => [
'banned_words' => array_values(array_filter(array_map(
'trim',
explode(',', (string) env('REVIEWS_BANNED_WORDS', '')),
))),
],
'cache_aggregates' => env('REVIEWS_CACHE_AGGREGATES', false),
'photos' => [
'enabled' => env('REVIEWS_PHOTOS_ENABLED', true),
'bucket' => env('REVIEWS_PHOTOS_BUCKET', 'photos'),
'disk' => env('REVIEWS_PHOTOS_DISK'),
'private_disk' => env('REVIEWS_PHOTOS_PRIVATE_DISK', 'local'),
'max' => env('REVIEWS_PHOTOS_MAX', 5),
'max_file_size' => env('REVIEWS_PHOTOS_MAX_FILE_SIZE', 5 * 1024 * 1024),
'accepted_mime_types' => ['image/jpeg', 'image/png', 'image/webp', 'image/gif'],
'responsive_widths' => [320, 640, 1024],
'visibility' => env('REVIEWS_PHOTOS_VISIBILITY', 'public'),
'warm_on_approval' => env('REVIEWS_PHOTOS_WARM_ON_APPROVAL', true),
],
];Every key
| Key | Default | Env | Purpose |
|---|---|---|---|
model | Review::class | — | Review model; point it at your subclass to extend it. Any other class throws InvalidConfigurationException. |
vote_model | ReviewVote::class | — | Helpful-vote model; point it at your subclass to extend it. Any other class throws InvalidConfigurationException. |
key_type | bigint | REVIEWS_KEY_TYPE | Key type of the reviewable, author and voter morph columns — bigint, uuid or ulid (anything else throws InvalidConfigurationException). Read when the migrations run. |
min_rating | 1 | REVIEWS_MIN_RATING | Lowest allowed rating, 0–255 (the rating column is an unsigned tinyint). |
max_rating | 5 | REVIEWS_MAX_RATING | Highest allowed rating, from min_rating (at least 1) to 255. |
default_status | pending | — | Status a review lands in when the moderator leaves it undecided (and auto-approve is off). Any value that isn’t a ReviewStatus throws. |
auto_approve | false | REVIEWS_AUTO_APPROVE | Approve new — and re-moderated, edited — reviews immediately, skipping the moderator. |
reset_status_on_edit | true | REVIEWS_RESET_STATUS_ON_EDIT | On an edit to the rating, title or content, send an undecided review back to default_status (false: keep its status). The moderator re-runs either way. |
one_per_author | false | REVIEWS_ONE_PER_AUTHOR | Block a second non-deleted review by the same author for the same subject; owner responses don’t count. Checked under the author’s row lock. |
register_facade_alias | true | REVIEWS_REGISTER_FACADE_ALIAS | Register the global Reviews alias; any other string is used as the alias name. false/0/off/no (or an explicit null) skip it; a blank value is not set, so the alias is registered. |
moderator | NullModerator::class | — | ReviewModerator consulted on create and on edits to the rating, title or content; swap in WordListModerator or your own. A class that doesn’t implement ReviewModerator throws. |
moderation.banned_words | [] | REVIEWS_BANNED_WORDS | Comma-separated words the WordListModerator rejects on. Set in PHP, it must be a list of non-empty strings. |
cache_aggregates | false | REVIEWS_CACHE_AGGREGATES | Keep cached reviews_count / reviews_avg in sync on opted-in reviewables. |
photos.enabled | true | REVIEWS_PHOTOS_ENABLED | Master switch for review photos; when false the bucket is never declared and withPhoto() throws. |
photos.bucket | photos | REVIEWS_PHOTOS_BUCKET | The media bucket photos are stored in (a string; blank = not set → photos). |
photos.disk | null | REVIEWS_PHOTOS_DISK | Disk for every photo. Not set (null or blank) = by visibility: private → photos.private_disk, public → media-library’s default disk. |
photos.private_disk | local | REVIEWS_PHOTOS_PRIVATE_DISK | Non-public disk for private photos and their variants when photos.disk is not set (a string; blank = not set → local). |
photos.max | 5 | REVIEWS_PHOTOS_MAX | Per-review photo limit, at least 0 (0 = unlimited, only when set explicitly); overflow throws tooManyPhotos(). |
photos.max_file_size | 5242880 | REVIEWS_PHOTOS_MAX_FILE_SIZE | Largest accepted upload, in bytes (5 MB), at least 0 (0 = no per-photo cap, only when set explicitly). |
photos.accepted_mime_types | jpeg, png, webp, gif | — | Allowed image mime types ([] accepts any type). |
photos.responsive_widths | [320, 640, 1024] | — | Responsive width ladder of positive integers (null uses the media-library default). |
photos.visibility | public | REVIEWS_PHOTOS_VISIBILITY | public or private (exact, lowercase); anything else throws rather than publishing the photos. |
photos.warm_on_approval | true | REVIEWS_PHOTOS_WARM_ON_APPROVAL | Queue variant generation when a review is approved. |
Every bool switch is read strictly: true/1/on/yes turn it on, false/0/off/no turn it off, a blank value (REVIEWS_PHOTOS_ENABLED=) is not set so the default applies, and anything else (say REVIEWS_PHOTOS_ENABLED=disabled) throws InvalidConfigurationException instead of quietly reading as the default.
Every other setting is read just as strictly. A setting that is not set — absent, null, or blank like a host’s REVIEWS_PHOTOS_DISK= — takes its default (photos.disk stays unset). Integers accept an int or a plain integer string (“10”, as every env value is a string); five, 5.5 or 1e3 throws rather than becoming 0 — which for photos.max would have meant unlimited photos. A photos.visibility typo such as privat throws rather than making the photos public, and a non-string bucket or disk name, a non-list mime-type or width setting, or a junk entry in one throws too.
Environment
Most switches are env-driven, so you rarely need to publish the file at all:
REVIEWS_MIN_RATING=1
REVIEWS_MAX_RATING=10
REVIEWS_AUTO_APPROVE=false
REVIEWS_ONE_PER_AUTHOR=true
REVIEWS_BANNED_WORDS=spam,scam
REVIEWS_CACHE_AGGREGATES=true
REVIEWS_PHOTOS_MAX=3
REVIEWS_PHOTOS_VISIBILITY=privateREVIEWS_BANNED_WORDS is split on commas and trimmed, so spaces around the words are harmless.
Inspecting the live configuration
The package adds a Reviews section to Laravel’s about command — models, rating scale, moderation switches, aggregates and photo settings:
php artisan about --only=reviewsIt reports switches, bounds and presence only: the banned-word list appears as a count and the photo disk as SET or DEFAULT, so the output is safe to paste into a ticket. A broken rating scale, default status, banned-word list or photo setting renders as INVALID instead of failing the command.
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 cryptoBy 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.