Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Reportable Laravel Package

paxha/laravel-reportable

A Laravel package that adds reportable behavior to Eloquent models, letting users flag or report content and store those reports through simple traits and relationships. Keep it plain text, around 300 chars.

Deep Wiki
Context7

Getting Started

1. Install and check compatibility first. Before anything else, open the package's composer.json and confirm its illuminate/* constraint against your app's Laravel version. With a last release in December 2020, the realistic ceiling is Laravel 7/8 — on Laravel 9+ you will likely need --ignore-platform-reqs tricks or a fork. Do this before writing a single line of application code, because it determines whether this is a dependency or a reference implementation you copy into app/Support/.

composer require paxha/laravel-reportable
php artisan vendor:publish --provider="Paxha\Reportable\ReportableServiceProvider"

If no config file appears, the package is config-free and everything is driven by the trait's properties/methods — read src/ directly.

2. Where to look first, in this order:

  • src/ — the trait itself. This is the entire API surface; documentation for a 12-star 2020 package is usually a README with one example, so the source is the spec.
  • config/reportable.php (if published) — date column name, date format, default grouping.
  • tests/ — if present, the tests are the only trustworthy usage examples and they show the expected Carbon types (string vs. Carbon vs. DateTime).
  • composer.json — version constraints, as above.

3. First use case: a daily order count over a date range.

use Paxha\Reportable\Traits\Reportable;

class Order extends Model
{
    use Reportable;

    protected $reportableDateColumn = 'created_at'; // confirm the real property name in src/
}

Then call the trait's date-range aggregation method (typically something like getReportByDateRange($from, $to)) from a tinker session, not from a controller. Tinker is the fastest way to discover what the return type actually is — an Eloquent Collection, a raw array, or a query builder — and that return shape dictates every downstream decision.

Start with the narrowest possible question: "how many records per day between two dates, for one model." Resist the urge to build a dashboard first; you need to learn the return shape and the boundary semantics before you can trust the numbers.

Implementation Patterns

Trait-on-model, but call it from a service. Put Reportable on the model (it needs the table and connection), but expose a ReportService or dedicated Reports\OrderReport class that wraps the trait calls. Two reasons: the trait's API is undocumented and may change if you fork it, and report queries tend to grow filters (tenant, status, channel) that don't belong on the model.

class OrderReport
{
    public function daily(Carbon $from, Carbon $to, array $filters = []): Collection
    {
        return Order::query()
            ->when($filters['status'] ?? null, fn ($q, $s) => $q->where('status', $s))
            ->getReportByDateRange($from, $to);
    }
}

Always chain onto a query builder, never call the trait statically on a bare model. Most date-reporting traits operate on the existing query, so Order::where('tenant_id', $id)->getReport...() scopes correctly, while a static call may silently ignore your filters. Verify this in the source — it's the single most common source of wrong numbers.

Zero-fill the series yourself. Date-grouped SQL only returns buckets that have rows. A week with no Sunday orders will produce a six-point chart, not seven. Post-process in PHP:

$byDate = $rows->keyBy(fn ($r) => Carbon::parse($r->date)->toDateString());
$series = collect($period)->map(fn ($day) => [
    'date'  => $day->toDateString(),
    'total' => $byDate->get($day->toDateString())->aggregate ?? 0,
]);

Cache aggressively, key on the arguments. Date-range reports are expensive and highly cacheable. Key on model, range, filters, and now()->format('Y-m-d-H') for same-day buckets that are still moving.

Keep the reporting layer SQL-dialect aware. Grouping by day/week/month is where portability dies: MySQL uses DATE_FORMAT, Postgres DATE_TRUNC/TO_CHAR, SQLite strftime. If the package hardcodes one, wrap it — you do not want to discover this when CI runs against SQLite.

Integration tip: return DTOs or arrays from your report service, not Eloquent models. Charts, CSV exports, and API resources all want plain data, and it keeps the package's return shape from leaking into your controllers.

Gotchas and Tips

Treat it as unmaintained. Twelve stars and a 2020 release means no security patches and no PHP 8.x/Laravel 10+ fixes. Two safe options: pin the exact version in composer.json with a comment, or copy the trait into app/Support/Reportable.php and own it. The second is usually better — the trait is small and the maintenance cost is near zero, whereas a stale dependency will eventually block a framework upgrade.

Date boundaries are inclusive/exclusive until proven otherwise. whereBetween($from, $to) on a DATETIME column silently excludes anything after $to 00:00:00. Pass $to->endOfDay() or use where('created_at', '<', $to->copy()->addDay()). Write a test that asserts a record at 23:59:59 on the last day is counted.

Timezone drift is the classic reporting bug. If the DB stores UTC and you group by DATE(created_at), "today's orders" shifts by your offset. Either convert in SQL (CONVERT_TZ) or, more simply, store a report_date column populated on write, or group by an explicit offset-adjusted expression. Never mix Carbon::today() (app tz) with raw DB date grouping without deciding which one wins.

Add an index on the date column. The package will do a range scan; without an index on (tenant_id, created_at) a dashboard query goes from milliseconds to seconds as the table grows. This is the highest-ROI change you'll make.

Global scopes and soft deletes apply. SoftDeletes excludes trashed rows from counts — sometimes desired (revenue), sometimes catastrophic (audit totals). Check withTrashed() support and test both.

Config caching in production. If you publish a config file, php artisan config:cache will freeze it; config() calls inside the trait at runtime may then behave differently than in local dev. Test with the cache enabled before deploying.

Verify the grouping input is not interpolated into raw SQL. Old reporting packages frequently build GROUP BY DATE_FORMAT(col, ?) from a user-supplied format string. If you expose a granularity parameter (day/week/month), whitelist it against a fixed array rather than passing it through.

Extension point: if the trait's date-column resolution is hardcoded to created_at, look for a getReportableDateColumn() method or property in src/ — overriding it in your model is cleaner than a fork. If it's truly hardcoded, that's your fork trigger.

Debugging tactic: log the underlying SQL with DB::listen() around a single trait call. With undocumented reporting helpers this is faster than reading the source, and it immediately reveals boundary and dialect issues.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package