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

Database schema & key types

One table backs the package. Its name comes from the table config key:

ColumnTypeNotes
idbigint, uuid or ulidPrimary key per primary_key_type — an auto-incrementing bigint by default, whatever key_type says.
reporter_type / reporter_idnullable morphThe reporter — null for guest and anonymous reports.
reported_type / reported_idnullable morphThe reported subject.
resolved_by_type / resolved_by_idnullable morphThe moderator who settled the report — null for system actions.
statusstring, indexedStatus value, default pending.
reasonstring, indexedThe reason slug.
descriptiontext, nullableThe reporter’s description.
resolution_notetext, nullableThe moderator’s note.
guest_identifierstring, nullable, indexedHashed IP or email used to dedupe guest reports.
resolved_attimestamp, nullableWhen the report was resolved or rejected.
created_at / updated_attimestampsStandard Eloquent timestamps.
deleted_attimestamp, nullableSoft 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_typeMorph id columnsUse when your models use
bigintbigintAuto-incrementing integer keys (the default).
uuiduuidUUID primary keys (HasUuids).
ulidchar(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=uuid
use 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=uuid

key_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 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.