Database schema & key types
One table backs the package. Its name comes from the table config key:
| Column | Type | Notes |
|---|---|---|
id | bigint, uuid or ulid | Primary key per primary_key_type — an auto-incrementing bigint by default, whatever key_type says. |
reporter_type / reporter_id | nullable morph | The reporter — null for guest and anonymous reports. |
reported_type / reported_id | nullable morph | The reported subject. |
resolved_by_type / resolved_by_id | nullable morph | The moderator who settled the report — null for system actions. |
status | string, indexed | Status value, default pending. |
reason | string, indexed | The reason slug. |
description | text, nullable | The reporter’s description. |
resolution_note | text, nullable | The moderator’s note. |
guest_identifier | string, nullable, indexed | Hashed IP or email used to dedupe guest reports. |
resolved_at | timestamp, nullable | When the report was resolved or rejected. |
created_at / updated_at | timestamps | Standard Eloquent timestamps. |
deleted_at | timestamp, nullable | Soft deletes. |
Two composite indexes serve the hot paths: (reported_type, reported_id, status) for aggregation and threshold queries, and a dedupe index on (reported_type, reported_id, reporter_type, reporter_id) named <table>_dedupe_index.
Morph key type
key_type types the id column of all three morph pairs — reporter, reported and resolved_by. The table’s own primary key is set separately, by primary_key_type (below):
| key_type | Morph id columns | Use when your models use |
|---|---|---|
bigint | bigint | Auto-incrementing integer keys (the default). |
uuid | uuid | UUID primary keys (HasUuids). |
ulid | char(26) | ULID primary keys (HasUlids). |
When key_type is uuid or ulid, your reporter, reportable and resolver models must use string primary keys. Set it before you migrate — it decides the column types:
REPORTS_KEY_TYPE=uuiduse Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Reports\Traits\GivesReports;
class User extends Model
{
use GivesReports;
use HasUuids; // string primary key to match key_type = uuid
}Unset or blank reads as bigint; an unrecognised value throws the toolkit’s InvalidConfigurationException when the migration runs, rather than silently building bigint columns.
Primary key type
primary_key_type types the reports table’s own id — the key other tables point at: bigint (the default, auto-incrementing), uuid or ulid. On uuid or ulid the Report model mints a UUIDv7 or a ULID on create, and route binding treats a malformed id as not found. Like key_type, it is read when the migration runs, and an unrecognised value throws InvalidConfigurationException.
Moderation is where it matters. A moderated report is an approvals subject, and approvals-for-laravel keys its subject columns by the same approvals.key_type as its moderator columns, so on PostgreSQL the two must match — with a bigint report id and UUID-keyed moderators, opening moderation fails. A host with UUID moderators sets both before migrating:
APPROVALS_KEY_TYPE=uuid
REPORTS_PRIMARY_KEY_TYPE=uuidkey_type still follows the reporter, reported and resolver models; the resolver is a moderator, so with UUID moderators it is uuid too. UUID moderation on PostgreSQL also needs approvals-for-laravel’s 0007 migration, which turns approvals.approval_request_id into a bigint — publish the approvals migrations again if you published them before it existed. MySQL and SQLite don’t fail on a mismatch, but matching the key types is still the safe setup.
Converting an existing install
The key type is fixed when the migration first runs: changing primary_key_type later does nothing to an existing table, and pointing the model at uuid or ulid over a bigint id column breaks every new report. There is no automatic conversion. Write a migration of your own, with writes stopped and a backup taken:
- Add a nullable uuid (or ulid) column to the reports table and fill it for every row with Str::uuid7() or Str::ulid(), keeping a map of old id → new id.
- Rewrite every reference to a report through the map: approval_requests.subject_id and approvals.approvable_id where the type column holds the report’s morph class, plus any column of your own that stores a report id. If APPROVALS_KEY_TYPE changes too, convert approvals’ own morph columns in the same migration.
- Drop the old id primary key, rename the new column to id and make it the primary key.
- Set REPORTS_PRIMARY_KEY_TYPE to the new type and deploy.
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.