Database schema & key types
Two tables back the package. reviews — the configured reviews.model’s table — holds reviews and owner responses alike:
| Column | Type | Notes |
|---|---|---|
id | bigint | Auto-incrementing primary key. |
parent_id | bigint, nullable | Self-referencing foreign key to the same table — set on owner responses. Cascades on delete once cascade_review_responses_on_delete has run (null on delete before). |
reviewable_type / reviewable_id | morph, nullable | The subject; id typed by key_type. |
author_type / author_id | morph, nullable | The author; id typed by key_type. |
rating | unsigned tinyint, nullable, indexed | null = a text-only review or a response. |
status | string, indexed | pending, approved or rejected; defaults to pending. |
verified | boolean, indexed | Verified purchase / reviewer; defaults to false. |
title | string, nullable | Optional headline. |
content | text, nullable | Optional body — a rating-only review has none. |
meta | jsonb, nullable | Free-form metadata, cast to a Collection; holds rejection_reason. |
helpful_count / unhelpful_count | unsigned int | Denormalized vote tallies; default 0. |
approved_at | timestamp, nullable | Stamped on approval, cleared on rejection. |
created_at / updated_at / deleted_at | timestamps | Reviews soft-delete. |
A composite index on (reviewable_type, reviewable_id, status) serves the per-subject aggregate queries. The rating column is an unsigned tiny integer, so keep max_rating at 255 or below.
A new Review instance carries the column defaults in memory — pending, unverified, both tallies 0 — so a just-created review or response serialises and compares like a fresh read.
review_votes
- review_id — foreign key to the configured review model’s table, cascading on delete.
- voter_type / voter_id — the polymorphic voter, typed by key_type.
- helpful — true for helpful, false for unhelpful.
- A unique index on (review_id, voter_type, voter_id): one vote per voter per review.
Key types
Authors, subjects and voters are polymorphic, so any Eloquent model can take part. key_type types their id columns; the package’s own tables always keep auto-incrementing keys:
| key_type | Use when your authors, subjects and voters… |
|---|---|
bigint | use Laravel’s default auto-incrementing keys (the default) |
uuid | use HasUuids |
ulid | use HasUlids |
All your morph targets must share one key type. Set it before you run the migrations — it is read when they run:
# Set before you run the migrations — the morph columns are typed from it
REVIEWS_KEY_TYPE=uuidAny other value throws InvalidConfigurationException instead of quietly migrating as bigint.
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.