Getting Started
Install with Composer and let Laravel auto-discover the service provider:
composer require abordage/eloquent-percentile
Start with the package README and the test suite to confirm the exact query method signature and supported database drivers. The practical first use case is a reporting query: calculate a median or P90 for a numeric column without hydrating every row into PHP.
$p90 = Order::query()
->where('created_at', '>=', now()->subMonth())
->percentile('total', 0.90); // confirm exact signature in README
Version note: 2.1.0 adds Laravel 13 support and stops CI-testing Laravel 11. If you are on Laravel 11, treat this release as untested until you validate it yourself or pin to 2.0.x.
Implementation Patterns
- Use percentile queries as reporting aggregates, not as replacements for normal Eloquent retrieval. A typical workflow is: apply filters with existing scopes, then call the package’s percentile aggregate as the final step.
- Keep percentile logic in a small reporting service or action class rather than scattering it through controllers. Example:
OrderMetrics::p90OrderValue($from, $to).
- Compose with existing query constraints:
$query = Order::query()
->where('tenant_id', $tenantId)
->whereBetween('created_at', [$from, $to]);
$p50 = $query->clone()->percentile('total', 0.50);
$p95 = $query->clone()->percentile('total', 0.95);
- For dashboards, cache aggregate results by tenant/date range/filter fingerprint. Percentile calculations can scan many rows and are usually too expensive for every page load.
- For high-traffic reporting, materialize percentiles into a metrics table via a scheduled command. Keep the raw percentile query in one place so the command and ad-hoc reporting use the same logic.
- Test with small, deterministic fixtures where you can calculate the expected percentile manually from a sorted array. This catches percentile-scale mistakes and driver differences early.
- Use query scopes for shared filters such as tenant, date range, status, and soft-delete behavior, then apply the percentile aggregate last.
Gotchas and Tips
- Confirm database support before adopting. Percentile SQL is not portable across all Laravel drivers; the package’s CI workflow uses PostgreSQL, so do not assume MySQL, MariaDB, or SQLite parity without testing.
- Check the percentile argument scale: packages commonly expect
0.90 rather than 90, or vice versa. A wrong scale can silently produce surprising results or database errors.
- Apply filters before the aggregate. Global scopes, soft deletes, and tenant constraints still matter; use
withTrashed() or withoutGlobalScopes() deliberately when reporting on excluded rows.
- Null handling is database-dependent. If nulls should count as zero, use
COALESCE in the aggregate expression; otherwise document that nulls are excluded.
- Grouped percentiles can behave differently from scalar percentiles. If you need per-tenant or per-category percentiles, verify whether the package returns multiple rows or requires raw
selectRaw SQL.
- Percentile results often come back as numeric strings or decimals. Cast before strict comparisons or JSON responses.
- Debug with
toSql() and getBindings(), then compare against a hand-written SQL percentile query. This quickly isolates query-builder issues from driver issues.
- Upgrade path: Laravel 12 users should find 2.1.0 low-risk. Laravel 11 users should validate against their test suite or stay on 2.0.x, since 2.1.0 no longer tests Laravel 11. The Dependabot action bumps in 2.1.0 do not affect runtime behavior.