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

Configuration

The package works with zero published configuration — the defaults are merged in automatically. The published config/reports.php:

return [
    'model' => RoundlyConsulting\Reports\Models\Report::class,
    'table' => 'reports',
    'key_type' => env('REPORTS_KEY_TYPE', 'bigint'),
    'default_reason' => 'other',
    'reasons' => ['spam', 'abuse', 'harassment', 'inappropriate', 'misinformation', 'other'],
    'allow_unknown_reasons' => false,
    'prevent_duplicates' => true,
    'duplicate_scope' => 'open',
    'strict_transitions' => true,
    'threshold' => null,
    'prune_after_days' => null,
    'moderation' => [
        'default_rule' => 'unanimous',
        'default_quorum' => null,
    ],
];

Every key

KeyDefaultPurpose
modelReport::classThe Eloquent model that stores reports. Point it at your own Report subclass; anything else throws InvalidConfigurationException.
tablereportsThe table reports live in — read by the model and by the migration. A string; a blank value is not set, so reports applies.
key_typebigintMorph key type for reporter, reported and resolved_by: bigint, uuid or ulid. Env REPORTS_KEY_TYPE; unset or blank reads as bigint, any other value throws InvalidConfigurationException.
default_reasonotherThe reason used when a report is filed without one (a string; a blank value is not set, so other applies).
reasonsthe six Reason slugsThe allowed reason slugs — a non-empty list of non-empty strings; a blank value is not set, so the Reason slugs apply. Add your own custom slugs here.
allow_unknown_reasonsfalseWhen true, any reason slug is accepted without validation.
prevent_duplicatestrueStop the same reporter or guest from reporting the same subject twice. Filings against one subject are serialized, so a double-submit can’t slip through.
duplicate_scopeopenopen dedupes against open reports only; any against every report ever filed. Any other value throws.
strict_transitionstrueAllow only declared status transitions; illegal moves throw.
thresholdnullReportThresholdReached fires when a new report brings a subject’s open count to exactly this number — once per crossing. null, blank (not set) or 0 disables it; anything else that is not a whole number throws InvalidConfigurationException when a report is filed.
prune_after_daysnullDefault age in days for Reports::prune() / reports:prune when no window is given. null or blank (not set) requires one — MissingPruneWindowException, never a zero-day window; anything else that is not a whole number throws InvalidConfigurationException.
moderation.default_ruleunanimousDefault ApprovalRule for Reports::moderate() unless ->rule() is called: unanimous, quorum, any or weighted. An unknown value throws InvalidConfigurationException when Reports::moderate() is called.
moderation.default_quorumnullApproval count (at least 1) of the quorum rule unless ->quorum() is called. null or blank (not set) requires every declared moderator; any other non-integer throws InvalidConfigurationException.

The moderation block seeds the Reports::moderate() builder. An unrecognised default_rule, or a default_quorum that isn’t a whole number of at least 1, throws InvalidConfigurationException rather than falling back to the strictest setting.

Environment

Only the key type is env-backed:

REPORTS_KEY_TYPE=uuid

Values read from .env arrive as strings and are read as such: the integer keys accept integer strings ('5') and the switches accept 'true'/'false', '1'/'0', 'on'/'off' and 'yes'/'no' — anything else throws. A key that is not set — absent, null, or blank like a host’s KEY= — takes its default; for the optional threshold, prune_after_days and moderation.default_quorum that means unset (no threshold, an explicit window required, every moderator), never 0. A value that is set but unusable throws the toolkit’s RoundlyConsulting\PackageToolkit\Exceptions\InvalidConfigurationException rather than quietly picking a side, and php artisan about shows it as INVALID.

  • A threshold, prune_after_days or moderation.default_quorum that is not a whole number (“two”, “1.5”).
  • A duplicate_scope other than open or any, or an unknown moderation.default_rule.
  • A non-string table or default_reason.
  • A reasons list that is empty or holds a non-string slug.
  • A model that is not Report or a subclass of it.

Set table and key_type before you migrate — both are read when the migration runs. To check what is active, php artisan about includes a Reports section (see Artisan commands).

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.