PHPackages                             reportshq/laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. reportshq/laravel

ActiveLibrary

reportshq/laravel
=================

Send a Laravel application's own events to ReportsHQ, as the standard taxonomy.

v0.1.0(2d ago)03↑1900%MITPHPPHP ^8.2

Since Aug 16Pushed yesterdayCompare

[ Source](https://github.com/ReportsHQ/laravel)[ Packagist](https://packagist.org/packages/reportshq/laravel)[ Docs](https://reportshq.org)[ RSS](/packages/reportshq-laravel/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (3)Versions (2)Used By (0)

reportshq/laravel
=================

[](#reportshqlaravel)

Send a Laravel application's own events to [ReportsHQ](https://reportshq.org). Your app keeps firing the events it already fires; this package listens, translates them into the reserved taxonomy, and ships them after the response has gone out. Then the reports build themselves.

Install
-------

[](#install)

```
composer require reportshq/laravel
```

```
# .env
REPORTSHQ_KEY=rhq_your_project_key
```

That is the whole integration for signups and sign-ins: the package listens to Laravel's own `Registered`, `Login` and `Logout` events, which your application already fires without writing anything.

For your own models, name them once:

```
// app/Providers/AppServiceProvider.php
use App\Models\Order;
use ReportsHQ\Laravel\ReportsHQ;

public function boot(): void
{
    app(ReportsHQ::class)->observe(Order::class, 'Order');
}
```

The second argument is the name in the mapping table, so orders living in `App\Models\Purchase` still map onto `commerce.order.created` without renaming anything.

What gets sent
--------------

[](#what-gets-sent)

Only the events in [the taxonomy](https://github.com/stacksjs/reportshq/blob/main/docs/events.md). An application fires dozens of events that mean nothing to a reporting taxonomy, and forwarding them under invented names would fill your project with vocabulary no report template can read, so anything unmapped is ignored.

Your app firesReportsHQ receives`Illuminate\Auth\Events\Registered``user.registered``Illuminate\Auth\Events\Login` / `Logout``user.login` / `user.logout``Order:created` / `:paid` / `:refunded` / `:cancelled`the matching `commerce.order.*``Checkout:started`, `Cart:updated`, `Product:viewed``commerce.checkout.started`, `commerce.cart.updated`, `commerce.product.viewed``Customer:created``commerce.customer.created``Subscription:created` / `:cancelled``user.subscription.started` / `.cancelled``Post:published` / `Post:viewed` / `Comment:created`the matching `cms.*`The subject is taken from `user_key` / `user_id` / `customer_id` and `session_key` / `session_id`. Send a **stable internal id**, never an email or a name: it is only ever compared for equality, so anything more identifying is data nobody needed.

Configuration
-------------

[](#configuration)

```
php artisan vendor:publish --tag=reportshq-config
```

```
// config/reportshq.php
return [
    'key' => env('REPORTSHQ_KEY', ''),
    'endpoint' => env('REPORTSHQ_ENDPOINT', 'https://reportshq.org/ingest'),
    'domains' => ['commerce' => true, 'users' => true, 'cms' => true],
    'sample_rate' => 1.0,
    'queue' => env('REPORTSHQ_QUEUE'),
];
```

**With no key set, the package registers nothing**: no listeners, no terminating callback, no requests. The same code runs in tests and on a laptop without sending anything or complaining about it.

Delivery happens after the response
-----------------------------------

[](#delivery-happens-after-the-response)

PHP has no long-lived process to flush from, so a buffer would otherwise die with the request. Events are delivered from Laravel's `terminating` callback, which runs after the response has already been sent to the browser: the person is looking at the page while this happens.

Naming a `queue` moves delivery off the web process entirely, which is what a busy application should do. The batch travels as plain arrays rather than as models, so what gets queued is exactly what gets sent and cannot change between the two.

It cannot slow your app down
----------------------------

[](#it-cannot-slow-your-app-down)

- `track` appends to an in-memory buffer and returns.
- The buffer is bounded. At the limit the **oldest** events are dropped, because if delivery has been failing, the recent events are the ones describing what is happening now.
- Failures never throw into your code. A sender that raises is caught, reported through `on_error`, and treated as a failed attempt.
- `5xx` and `429` retry with backoff. `4xx` does not: a bad key is bad every time, and retrying is a slower way to fail while blocking everything behind it.
- The queued job does not retry on top of that. Two retry policies multiply out to a lot of requests to an endpoint that has already said no.

Sampling keeps subjects whole
-----------------------------

[](#sampling-keeps-subjects-whole)

`sample_rate` keeps a fraction of **subjects**, not of events. Sampling events independently is the obvious implementation and it quietly ruins the reports it feeds: a funnel asks how many people who viewed a product went on to check out, and if each of those events is kept by its own coin flip, the steps stop belonging to the same people and every conversion rate becomes noise.

Subjects are hashed with the same FNV-1a the Stacks SDK uses, so the two agree about who is in a sample and an application migrating between them keeps a continuous history rather than a step change at the switchover.

Sending your own events
-----------------------

[](#sending-your-own-events)

```
app(ReportsHQ::class)->track([
    'name' => 'commerce.order.created',
    'value' => 4250,
    'currency' => 'USD',
    'user_key' => (string) $customer->id,
    'properties' => ['plan' => 'pro'],
]);
```

Both SDKs produce the same payloads
-----------------------------------

[](#both-sdks-produce-the-same-payloads)

`docs/fixtures/sdk-events.json` in the main repository states, for each logical event, the one taxonomy payload every SDK must produce. This package asserts it from PHP and the Stacks package asserts it from TypeScript. Without it the two drift, and the drift is invisible until somebody compares a Laravel app's reports with a Stacks app's.

Tests
-----

[](#tests)

```
php packages/laravel/tests/run.php
```

A plain runner rather than phpunit, and deliberately: the mapper, sampler, config and transport are plain PHP with no Illuminate dependency, which is what lets them be tested without booting an application. Running them needs `php` and nothing else - no composer install, no vendor directory, no network. The service provider's listener registration genuinely needs a booted application and is not covered here.

License
-------

[](#license)

MIT

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance100

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity35

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

2d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a97b94acce2703a63911978deee2cd58935b7df44ea8513125dfef8e48f2f079?d=identicon)[Chris1904](/maintainers/Chris1904)

---

Top Contributors

[![chrisbbreuer](https://avatars.githubusercontent.com/u/6228425?v=4)](https://github.com/chrisbbreuer "chrisbbreuer (7 commits)")

---

Tags

laraveleventsanalyticsreportshq

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/reportshq-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/reportshq-laravel/health.svg)](https://phpackages.com/packages/reportshq-laravel)
```

###  Alternatives

[qodenl/laravel-posthog

Laravel implementation for Posthog

35109.2k](/packages/qodenl-laravel-posthog)[ozankurt/google-analytics

Laravel Google Analytics

7617.3k](/packages/ozankurt-google-analytics)[classiebit/eventmie

Run your own Events business with Eventmie Pro. Use it as event ticket selling website or event management platform on your own domain.

1892.0k](/packages/classiebit-eventmie)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
